roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         
140         this.initEvents();
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165         //Roo.log(['addxtype', cn]);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr, is_body);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229        
230         return ret;
231     },
232     
233     
234     addxtypeChild : function (tree, cntr, is_body)
235     {
236         Roo.debug && Roo.log('addxtypeChild:' + cntr);
237         var cn = this;
238         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
239         
240         
241         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242                     (typeof(tree['flexy:foreach']) != 'undefined');
243           
244         
245         
246          skip_children = false;
247         // render the element if it's not BODY.
248         if (!is_body) {
249            
250             cn = Roo.factory(tree);
251            
252             cn.parentType = this.xtype; //??
253             cn.parentId = this.id;
254             
255             var build_from_html =  Roo.XComponent.build_from_html;
256             
257             
258             // does the container contain child eleemnts with 'xtype' attributes.
259             // that match this xtype..
260             // note - when we render we create these as well..
261             // so we should check to see if body has xtype set.
262             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
263                
264                 var self_cntr_el = Roo.get(this[cntr](false));
265                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
266                 if (echild) { 
267                     //Roo.log(Roo.XComponent.build_from_html);
268                     //Roo.log("got echild:");
269                     //Roo.log(echild);
270                 }
271                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272                 // and are not displayed -this causes this to use up the wrong element when matching.
273                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
274                 
275                 
276                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
278                   
279                   
280                   
281                     cn.el = echild;
282                   //  Roo.log("GOT");
283                     //echild.dom.removeAttribute('xtype');
284                 } else {
285                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286                     Roo.debug && Roo.log(self_cntr_el);
287                     Roo.debug && Roo.log(echild);
288                     Roo.debug && Roo.log(cn);
289                 }
290             }
291            
292             
293            
294             // if object has flexy:if - then it may or may not be rendered.
295             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
296                 // skip a flexy if element.
297                 Roo.debug && Roo.log('skipping render');
298                 Roo.debug && Roo.log(tree);
299                 if (!cn.el) {
300                     Roo.debug && Roo.log('skipping all children');
301                     skip_children = true;
302                 }
303                 
304              } else {
305                  
306                 // actually if flexy:foreach is found, we really want to create 
307                 // multiple copies here...
308                 //Roo.log('render');
309                 //Roo.log(this[cntr]());
310                 // some elements do not have render methods.. like the layouts...
311                 cn.render && cn.render(this[cntr](true));
312              }
313             // then add the element..
314         }
315         
316         
317         // handle the kids..
318         
319         var nitems = [];
320         /*
321         if (typeof (tree.menu) != 'undefined') {
322             tree.menu.parentType = cn.xtype;
323             tree.menu.triggerEl = cn.el;
324             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
325             
326         }
327         */
328         if (!tree.items || !tree.items.length) {
329             cn.items = nitems;
330             //Roo.log(["no children", this]);
331             
332             return cn;
333         }
334          
335         var items = tree.items;
336         delete tree.items;
337         
338         //Roo.log(items.length);
339             // add the items..
340         if (!skip_children) {    
341             for(var i =0;i < items.length;i++) {
342               //  Roo.log(['add child', items[i]]);
343                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
344             }
345         }
346         
347         cn.items = nitems;
348         
349         //Roo.log("fire childrenrendered");
350         
351         cn.fireEvent('childrenrendered', this);
352         
353         return cn;
354     },
355     /**
356      * Show a component - removes 'hidden' class
357      */
358     show : function()
359     {
360         if (this.el) {
361             this.el.removeClass('hidden');
362         }
363     },
364     /**
365      * Hide a component - adds 'hidden' class
366      */
367     hide: function()
368     {
369         if (this.el && !this.el.hasClass('hidden')) {
370             this.el.addClass('hidden');
371         }
372         
373     }
374 });
375
376  /*
377  * - LGPL
378  *
379  * Body
380  *
381  */
382
383 /**
384  * @class Roo.bootstrap.Body
385  * @extends Roo.bootstrap.Component
386  * Bootstrap Body class
387  *
388  * @constructor
389  * Create a new body
390  * @param {Object} config The config object
391  */
392
393 Roo.bootstrap.Body = function(config){
394
395     config = config || {};
396
397     Roo.bootstrap.Body.superclass.constructor.call(this, config);
398     this.el = Roo.get(config.el ? config.el : document.body );
399     if (this.cls && this.cls.length) {
400         Roo.get(document.body).addClass(this.cls);
401     }
402 };
403
404 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
405
406     is_body : true,// just to make sure it's constructed?
407
408         autoCreate : {
409         cls: 'container'
410     },
411     onRender : function(ct, position)
412     {
413        /* Roo.log("Roo.bootstrap.Body - onRender");
414         if (this.cls && this.cls.length) {
415             Roo.get(document.body).addClass(this.cls);
416         }
417         // style??? xttr???
418         */
419     }
420
421
422
423
424 });
425 /*
426  * - LGPL
427  *
428  * button group
429  * 
430  */
431
432
433 /**
434  * @class Roo.bootstrap.ButtonGroup
435  * @extends Roo.bootstrap.Component
436  * Bootstrap ButtonGroup class
437  * @cfg {String} size lg | sm | xs (default empty normal)
438  * @cfg {String} align vertical | justified  (default none)
439  * @cfg {String} direction up | down (default down)
440  * @cfg {Boolean} toolbar false | true
441  * @cfg {Boolean} btn true | false
442  * 
443  * 
444  * @constructor
445  * Create a new Input
446  * @param {Object} config The config object
447  */
448
449 Roo.bootstrap.ButtonGroup = function(config){
450     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
451 };
452
453 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
454     
455     size: '',
456     align: '',
457     direction: '',
458     toolbar: false,
459     btn: true,
460
461     getAutoCreate : function(){
462         var cfg = {
463             cls: 'btn-group',
464             html : null
465         };
466         
467         cfg.html = this.html || cfg.html;
468         
469         if (this.toolbar) {
470             cfg = {
471                 cls: 'btn-toolbar',
472                 html: null
473             };
474             
475             return cfg;
476         }
477         
478         if (['vertical','justified'].indexOf(this.align)!==-1) {
479             cfg.cls = 'btn-group-' + this.align;
480             
481             if (this.align == 'justified') {
482                 console.log(this.items);
483             }
484         }
485         
486         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
487             cfg.cls += ' btn-group-' + this.size;
488         }
489         
490         if (this.direction == 'up') {
491             cfg.cls += ' dropup' ;
492         }
493         
494         return cfg;
495     }
496    
497 });
498
499  /*
500  * - LGPL
501  *
502  * button
503  * 
504  */
505
506 /**
507  * @class Roo.bootstrap.Button
508  * @extends Roo.bootstrap.Component
509  * Bootstrap Button class
510  * @cfg {String} html The button content
511  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
512  * @cfg {String} size ( lg | sm | xs)
513  * @cfg {String} tag ( a | input | submit)
514  * @cfg {String} href empty or href
515  * @cfg {Boolean} disabled default false;
516  * @cfg {Boolean} isClose default false;
517  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
518  * @cfg {String} badge text for badge
519  * @cfg {String} theme default 
520  * @cfg {Boolean} inverse 
521  * @cfg {Boolean} toggle 
522  * @cfg {String} ontext text for on toggle state
523  * @cfg {String} offtext text for off toggle state
524  * @cfg {Boolean} defaulton 
525  * @cfg {Boolean} preventDefault  default true
526  * @cfg {Boolean} removeClass remove the standard class..
527  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
528  * 
529  * @constructor
530  * Create a new button
531  * @param {Object} config The config object
532  */
533
534
535 Roo.bootstrap.Button = function(config){
536     Roo.bootstrap.Button.superclass.constructor.call(this, config);
537     this.addEvents({
538         // raw events
539         /**
540          * @event click
541          * When a butotn is pressed
542          * @param {Roo.bootstrap.Button} this
543          * @param {Roo.EventObject} e
544          */
545         "click" : true,
546          /**
547          * @event toggle
548          * After the button has been toggles
549          * @param {Roo.EventObject} e
550          * @param {boolean} pressed (also available as button.pressed)
551          */
552         "toggle" : true
553     });
554 };
555
556 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
557     html: false,
558     active: false,
559     weight: '',
560     size: '',
561     tag: 'button',
562     href: '',
563     disabled: false,
564     isClose: false,
565     glyphicon: '',
566     badge: '',
567     theme: 'default',
568     inverse: false,
569     
570     toggle: false,
571     ontext: 'ON',
572     offtext: 'OFF',
573     defaulton: true,
574     preventDefault: true,
575     removeClass: false,
576     name: false,
577     target: false,
578     
579     
580     pressed : null,
581      
582     
583     getAutoCreate : function(){
584         
585         var cfg = {
586             tag : 'button',
587             cls : 'roo-button',
588             html: ''
589         };
590         
591         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
592             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
593             this.tag = 'button';
594         } else {
595             cfg.tag = this.tag;
596         }
597         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
598         
599         if (this.toggle == true) {
600             cfg={
601                 tag: 'div',
602                 cls: 'slider-frame roo-button',
603                 cn: [
604                     {
605                         tag: 'span',
606                         'data-on-text':'ON',
607                         'data-off-text':'OFF',
608                         cls: 'slider-button',
609                         html: this.offtext
610                     }
611                 ]
612             };
613             
614             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
615                 cfg.cls += ' '+this.weight;
616             }
617             
618             return cfg;
619         }
620         
621         if (this.isClose) {
622             cfg.cls += ' close';
623             
624             cfg["aria-hidden"] = true;
625             
626             cfg.html = "&times;";
627             
628             return cfg;
629         }
630         
631          
632         if (this.theme==='default') {
633             cfg.cls = 'btn roo-button';
634             
635             //if (this.parentType != 'Navbar') {
636             this.weight = this.weight.length ?  this.weight : 'default';
637             //}
638             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
639                 
640                 cfg.cls += ' btn-' + this.weight;
641             }
642         } else if (this.theme==='glow') {
643             
644             cfg.tag = 'a';
645             cfg.cls = 'btn-glow roo-button';
646             
647             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
648                 
649                 cfg.cls += ' ' + this.weight;
650             }
651         }
652    
653         
654         if (this.inverse) {
655             this.cls += ' inverse';
656         }
657         
658         
659         if (this.active) {
660             cfg.cls += ' active';
661         }
662         
663         if (this.disabled) {
664             cfg.disabled = 'disabled';
665         }
666         
667         if (this.items) {
668             Roo.log('changing to ul' );
669             cfg.tag = 'ul';
670             this.glyphicon = 'caret';
671         }
672         
673         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
674          
675         //gsRoo.log(this.parentType);
676         if (this.parentType === 'Navbar' && !this.parent().bar) {
677             Roo.log('changing to li?');
678             
679             cfg.tag = 'li';
680             
681             cfg.cls = '';
682             cfg.cn =  [{
683                 tag : 'a',
684                 cls : 'roo-button',
685                 html : this.html,
686                 href : this.href || '#'
687             }];
688             if (this.menu) {
689                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
690                 cfg.cls += ' dropdown';
691             }   
692             
693             delete cfg.html;
694             
695         }
696         
697        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
698         
699         if (this.glyphicon) {
700             cfg.html = ' ' + cfg.html;
701             
702             cfg.cn = [
703                 {
704                     tag: 'span',
705                     cls: 'glyphicon glyphicon-' + this.glyphicon
706                 }
707             ];
708         }
709         
710         if (this.badge) {
711             cfg.html += ' ';
712             
713             cfg.tag = 'a';
714             
715 //            cfg.cls='btn roo-button';
716             
717             cfg.href=this.href;
718             
719             var value = cfg.html;
720             
721             if(this.glyphicon){
722                 value = {
723                             tag: 'span',
724                             cls: 'glyphicon glyphicon-' + this.glyphicon,
725                             html: this.html
726                         };
727                 
728             }
729             
730             cfg.cn = [
731                 value,
732                 {
733                     tag: 'span',
734                     cls: 'badge',
735                     html: this.badge
736                 }
737             ];
738             
739             cfg.html='';
740         }
741         
742         if (this.menu) {
743             cfg.cls += ' dropdown';
744             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
745         }
746         
747         if (cfg.tag !== 'a' && this.href !== '') {
748             throw "Tag must be a to set href.";
749         } else if (this.href.length > 0) {
750             cfg.href = this.href;
751         }
752         
753         if(this.removeClass){
754             cfg.cls = '';
755         }
756         
757         if(this.target){
758             cfg.target = this.target;
759         }
760         
761         return cfg;
762     },
763     initEvents: function() {
764        // Roo.log('init events?');
765 //        Roo.log(this.el.dom);
766         // add the menu...
767         
768         if (typeof (this.menu) != 'undefined') {
769             this.menu.parentType = this.xtype;
770             this.menu.triggerEl = this.el;
771             this.addxtype(Roo.apply({}, this.menu));
772         }
773
774
775        if (this.el.hasClass('roo-button')) {
776             this.el.on('click', this.onClick, this);
777        } else {
778             this.el.select('.roo-button').on('click', this.onClick, this);
779        }
780        
781        if(this.removeClass){
782            this.el.on('click', this.onClick, this);
783        }
784        
785        this.el.enableDisplayMode();
786         
787     },
788     onClick : function(e)
789     {
790         if (this.disabled) {
791             return;
792         }
793         
794         
795         Roo.log('button on click ');
796         if(this.preventDefault){
797             e.preventDefault();
798         }
799         if (this.pressed === true || this.pressed === false) {
800             this.pressed = !this.pressed;
801             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
802             this.fireEvent('toggle', this, e, this.pressed);
803         }
804         
805         
806         this.fireEvent('click', this, e);
807     },
808     
809     /**
810      * Enables this button
811      */
812     enable : function()
813     {
814         this.disabled = false;
815         this.el.removeClass('disabled');
816     },
817     
818     /**
819      * Disable this button
820      */
821     disable : function()
822     {
823         this.disabled = true;
824         this.el.addClass('disabled');
825     },
826      /**
827      * sets the active state on/off, 
828      * @param {Boolean} state (optional) Force a particular state
829      */
830     setActive : function(v) {
831         
832         this.el[v ? 'addClass' : 'removeClass']('active');
833     },
834      /**
835      * toggles the current active state 
836      */
837     toggleActive : function()
838     {
839        var active = this.el.hasClass('active');
840        this.setActive(!active);
841        
842         
843     },
844     setText : function(str)
845     {
846         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
847     },
848     getText : function()
849     {
850         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
851     },
852     hide: function() {
853        
854      
855         this.el.hide();   
856     },
857     show: function() {
858        
859         this.el.show();   
860     }
861     
862     
863 });
864
865  /*
866  * - LGPL
867  *
868  * column
869  * 
870  */
871
872 /**
873  * @class Roo.bootstrap.Column
874  * @extends Roo.bootstrap.Component
875  * Bootstrap Column class
876  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
877  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
878  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
879  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
880  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
881  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
882  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
883  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
884  *
885  * 
886  * @cfg {Boolean} hidden (true|false) hide the element
887  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
888  * @cfg {String} fa (ban|check|...) font awesome icon
889  * @cfg {Number} fasize (1|2|....) font awsome size
890
891  * @cfg {String} icon (info-sign|check|...) glyphicon name
892
893  * @cfg {String} html content of column.
894  * 
895  * @constructor
896  * Create a new Column
897  * @param {Object} config The config object
898  */
899
900 Roo.bootstrap.Column = function(config){
901     Roo.bootstrap.Column.superclass.constructor.call(this, config);
902 };
903
904 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
905     
906     xs: false,
907     sm: false,
908     md: false,
909     lg: false,
910     xsoff: false,
911     smoff: false,
912     mdoff: false,
913     lgoff: false,
914     html: '',
915     offset: 0,
916     alert: false,
917     fa: false,
918     icon : false,
919     hidden : false,
920     fasize : 1,
921     
922     getAutoCreate : function(){
923         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
924         
925         cfg = {
926             tag: 'div',
927             cls: 'column'
928         };
929         
930         var settings=this;
931         ['xs','sm','md','lg'].map(function(size){
932             //Roo.log( size + ':' + settings[size]);
933             
934             if (settings[size+'off'] !== false) {
935                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
936             }
937             
938             if (settings[size] === false) {
939                 return;
940             }
941             
942             if (!settings[size]) { // 0 = hidden
943                 cfg.cls += ' hidden-' + size;
944                 return;
945             }
946             cfg.cls += ' col-' + size + '-' + settings[size];
947             
948         });
949         
950         if (this.hidden) {
951             cfg.cls += ' hidden';
952         }
953         
954         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
955             cfg.cls +=' alert alert-' + this.alert;
956         }
957         
958         
959         if (this.html.length) {
960             cfg.html = this.html;
961         }
962         if (this.fa) {
963             var fasize = '';
964             if (this.fasize > 1) {
965                 fasize = ' fa-' + this.fasize + 'x';
966             }
967             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
968             
969             
970         }
971         if (this.icon) {
972             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
973         }
974         
975         return cfg;
976     }
977    
978 });
979
980  
981
982  /*
983  * - LGPL
984  *
985  * page container.
986  * 
987  */
988
989
990 /**
991  * @class Roo.bootstrap.Container
992  * @extends Roo.bootstrap.Component
993  * Bootstrap Container class
994  * @cfg {Boolean} jumbotron is it a jumbotron element
995  * @cfg {String} html content of element
996  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
997  * @cfg {String} panel (primary|success|info|warning|danger) render as panel  - type - primary/success.....
998  * @cfg {String} header content of header (for panel)
999  * @cfg {String} footer content of footer (for panel)
1000  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1001  * @cfg {String} tag (header|aside|section) type of HTML tag.
1002  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1003  * @cfg {String} fa font awesome icon
1004  * @cfg {String} icon (info-sign|check|...) glyphicon name
1005  * @cfg {Boolean} hidden (true|false) hide the element
1006  * @cfg {Boolean} expandable (true|false) default false
1007  * @cfg {Boolean} expanded (true|false) default true
1008  * @cfg {String} rheader contet on the right of header
1009  * @cfg {Boolean} clickable (true|false) default false
1010
1011  *     
1012  * @constructor
1013  * Create a new Container
1014  * @param {Object} config The config object
1015  */
1016
1017 Roo.bootstrap.Container = function(config){
1018     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1019     
1020     this.addEvents({
1021         // raw events
1022          /**
1023          * @event expand
1024          * After the panel has been expand
1025          * 
1026          * @param {Roo.bootstrap.Container} this
1027          */
1028         "expand" : true,
1029         /**
1030          * @event collapse
1031          * After the panel has been collapsed
1032          * 
1033          * @param {Roo.bootstrap.Container} this
1034          */
1035         "collapse" : true,
1036         /**
1037          * @event click
1038          * When a element is chick
1039          * @param {Roo.bootstrap.Container} this
1040          * @param {Roo.EventObject} e
1041          */
1042         "click" : true
1043     });
1044 };
1045
1046 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1047     
1048     jumbotron : false,
1049     well: '',
1050     panel : '',
1051     header: '',
1052     footer : '',
1053     sticky: '',
1054     tag : false,
1055     alert : false,
1056     fa: false,
1057     icon : false,
1058     expandable : false,
1059     rheader : '',
1060     expanded : true,
1061     clickable: false,
1062   
1063      
1064     getChildContainer : function() {
1065         
1066         if(!this.el){
1067             return false;
1068         }
1069         
1070         if (this.panel.length) {
1071             return this.el.select('.panel-body',true).first();
1072         }
1073         
1074         return this.el;
1075     },
1076     
1077     
1078     getAutoCreate : function(){
1079         
1080         var cfg = {
1081             tag : this.tag || 'div',
1082             html : '',
1083             cls : ''
1084         };
1085         if (this.jumbotron) {
1086             cfg.cls = 'jumbotron';
1087         }
1088         
1089         
1090         
1091         // - this is applied by the parent..
1092         //if (this.cls) {
1093         //    cfg.cls = this.cls + '';
1094         //}
1095         
1096         if (this.sticky.length) {
1097             
1098             var bd = Roo.get(document.body);
1099             if (!bd.hasClass('bootstrap-sticky')) {
1100                 bd.addClass('bootstrap-sticky');
1101                 Roo.select('html',true).setStyle('height', '100%');
1102             }
1103              
1104             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1105         }
1106         
1107         
1108         if (this.well.length) {
1109             switch (this.well) {
1110                 case 'lg':
1111                 case 'sm':
1112                     cfg.cls +=' well well-' +this.well;
1113                     break;
1114                 default:
1115                     cfg.cls +=' well';
1116                     break;
1117             }
1118         }
1119         
1120         if (this.hidden) {
1121             cfg.cls += ' hidden';
1122         }
1123         
1124         
1125         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1126             cfg.cls +=' alert alert-' + this.alert;
1127         }
1128         
1129         var body = cfg;
1130         
1131         if (this.panel.length) {
1132             cfg.cls += ' panel panel-' + this.panel;
1133             cfg.cn = [];
1134             if (this.header.length) {
1135                 
1136                 var h = [];
1137                 
1138                 if(this.expandable){
1139                     
1140                     cfg.cls = cfg.cls + ' expandable';
1141                     
1142                     h.push({
1143                         tag: 'i',
1144                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1145                     });
1146                     
1147                 }
1148                 
1149                 h.push(
1150                     {
1151                         tag: 'span',
1152                         cls : 'panel-title',
1153                         html : (this.expandable ? '&nbsp;' : '') + this.header
1154                     },
1155                     {
1156                         tag: 'span',
1157                         cls: 'panel-header-right',
1158                         html: this.rheader
1159                     }
1160                 );
1161                 
1162                 cfg.cn.push({
1163                     cls : 'panel-heading',
1164                     style : this.expandable ? 'cursor: pointer' : '',
1165                     cn : h
1166                 });
1167                 
1168             }
1169             
1170             body = false;
1171             cfg.cn.push({
1172                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1173                 html : this.html
1174             });
1175             
1176             
1177             if (this.footer.length) {
1178                 cfg.cn.push({
1179                     cls : 'panel-footer',
1180                     html : this.footer
1181                     
1182                 });
1183             }
1184             
1185         }
1186         
1187         if (body) {
1188             body.html = this.html || cfg.html;
1189             // prefix with the icons..
1190             if (this.fa) {
1191                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1192             }
1193             if (this.icon) {
1194                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1195             }
1196             
1197             
1198         }
1199         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1200             cfg.cls =  'container';
1201         }
1202         
1203         return cfg;
1204     },
1205     
1206     initEvents: function() 
1207     {
1208         if(this.expandable){
1209             var headerEl = this.headerEl();
1210         
1211             if(headerEl){
1212                 headerEl.on('click', this.onToggleClick, this);
1213             }
1214         }
1215         
1216         if(this.clickable){
1217             this.el.on('click', this.onClick, this);
1218         }
1219         
1220     },
1221     
1222     onToggleClick : function()
1223     {
1224         var headerEl = this.headerEl();
1225         
1226         if(!headerEl){
1227             return;
1228         }
1229         
1230         if(this.expanded){
1231             this.collapse();
1232             return;
1233         }
1234         
1235         this.expand();
1236     },
1237     
1238     expand : function()
1239     {
1240         if(this.fireEvent('expand', this)) {
1241             
1242             this.expanded = true;
1243             
1244             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1245             
1246             this.el.select('.panel-body',true).first().removeClass('hide');
1247             
1248             var toggleEl = this.toggleEl();
1249
1250             if(!toggleEl){
1251                 return;
1252             }
1253
1254             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1255         }
1256         
1257     },
1258     
1259     collapse : function()
1260     {
1261         if(this.fireEvent('collapse', this)) {
1262             
1263             this.expanded = false;
1264             
1265             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1266             this.el.select('.panel-body',true).first().addClass('hide');
1267         
1268             var toggleEl = this.toggleEl();
1269
1270             if(!toggleEl){
1271                 return;
1272             }
1273
1274             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1275         }
1276     },
1277     
1278     toggleEl : function()
1279     {
1280         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1281             return;
1282         }
1283         
1284         return this.el.select('.panel-heading .fa',true).first();
1285     },
1286     
1287     headerEl : function()
1288     {
1289         if(!this.el || !this.panel.length || !this.header.length){
1290             return;
1291         }
1292         
1293         return this.el.select('.panel-heading',true).first()
1294     },
1295     
1296     titleEl : function()
1297     {
1298         if(!this.el || !this.panel.length || !this.header.length){
1299             return;
1300         }
1301         
1302         return this.el.select('.panel-title',true).first();
1303     },
1304     
1305     setTitle : function(v)
1306     {
1307         var titleEl = this.titleEl();
1308         
1309         if(!titleEl){
1310             return;
1311         }
1312         
1313         titleEl.dom.innerHTML = v;
1314     },
1315     
1316     getTitle : function()
1317     {
1318         
1319         var titleEl = this.titleEl();
1320         
1321         if(!titleEl){
1322             return '';
1323         }
1324         
1325         return titleEl.dom.innerHTML;
1326     },
1327     
1328     setRightTitle : function(v)
1329     {
1330         var t = this.el.select('.panel-header-right',true).first();
1331         
1332         if(!t){
1333             return;
1334         }
1335         
1336         t.dom.innerHTML = v;
1337     },
1338     
1339     onClick : function(e)
1340     {
1341         e.preventDefault();
1342         
1343         this.fireEvent('click', this, e);
1344     }
1345    
1346 });
1347
1348  /*
1349  * - LGPL
1350  *
1351  * image
1352  * 
1353  */
1354
1355
1356 /**
1357  * @class Roo.bootstrap.Img
1358  * @extends Roo.bootstrap.Component
1359  * Bootstrap Img class
1360  * @cfg {Boolean} imgResponsive false | true
1361  * @cfg {String} border rounded | circle | thumbnail
1362  * @cfg {String} src image source
1363  * @cfg {String} alt image alternative text
1364  * @cfg {String} href a tag href
1365  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1366  * @cfg {String} xsUrl xs image source
1367  * @cfg {String} smUrl sm image source
1368  * @cfg {String} mdUrl md image source
1369  * @cfg {String} lgUrl lg image source
1370  * 
1371  * @constructor
1372  * Create a new Input
1373  * @param {Object} config The config object
1374  */
1375
1376 Roo.bootstrap.Img = function(config){
1377     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1378     
1379     this.addEvents({
1380         // img events
1381         /**
1382          * @event click
1383          * The img click event for the img.
1384          * @param {Roo.EventObject} e
1385          */
1386         "click" : true
1387     });
1388 };
1389
1390 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1391     
1392     imgResponsive: true,
1393     border: '',
1394     src: 'about:blank',
1395     href: false,
1396     target: false,
1397     xsUrl: '',
1398     smUrl: '',
1399     mdUrl: '',
1400     lgUrl: '',
1401
1402     getAutoCreate : function()
1403     {   
1404         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1405             return this.createSingleImg();
1406         }
1407         
1408         var cfg = {
1409             tag: 'div',
1410             cls: 'roo-image-responsive-group',
1411             cn: []
1412         };
1413         var _this = this;
1414         
1415         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1416             
1417             if(!_this[size + 'Url']){
1418                 return;
1419             }
1420             
1421             var img = {
1422                 tag: 'img',
1423                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1424                 html: _this.html || cfg.html,
1425                 src: _this[size + 'Url']
1426             };
1427             
1428             img.cls += ' roo-image-responsive-' + size;
1429             
1430             var s = ['xs', 'sm', 'md', 'lg'];
1431             
1432             s.splice(s.indexOf(size), 1);
1433             
1434             Roo.each(s, function(ss){
1435                 img.cls += ' hidden-' + ss;
1436             });
1437             
1438             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1439                 cfg.cls += ' img-' + _this.border;
1440             }
1441             
1442             if(_this.alt){
1443                 cfg.alt = _this.alt;
1444             }
1445             
1446             if(_this.href){
1447                 var a = {
1448                     tag: 'a',
1449                     href: _this.href,
1450                     cn: [
1451                         img
1452                     ]
1453                 };
1454
1455                 if(this.target){
1456                     a.target = _this.target;
1457                 }
1458             }
1459             
1460             cfg.cn.push((_this.href) ? a : img);
1461             
1462         });
1463         
1464         return cfg;
1465     },
1466     
1467     createSingleImg : function()
1468     {
1469         var cfg = {
1470             tag: 'img',
1471             cls: (this.imgResponsive) ? 'img-responsive' : '',
1472             html : null,
1473             src : 'about:blank'  // just incase src get's set to undefined?!?
1474         };
1475         
1476         cfg.html = this.html || cfg.html;
1477         
1478         cfg.src = this.src || cfg.src;
1479         
1480         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1481             cfg.cls += ' img-' + this.border;
1482         }
1483         
1484         if(this.alt){
1485             cfg.alt = this.alt;
1486         }
1487         
1488         if(this.href){
1489             var a = {
1490                 tag: 'a',
1491                 href: this.href,
1492                 cn: [
1493                     cfg
1494                 ]
1495             };
1496             
1497             if(this.target){
1498                 a.target = this.target;
1499             }
1500             
1501         }
1502         
1503         return (this.href) ? a : cfg;
1504     },
1505     
1506     initEvents: function() 
1507     {
1508         if(!this.href){
1509             this.el.on('click', this.onClick, this);
1510         }
1511         
1512     },
1513     
1514     onClick : function(e)
1515     {
1516         Roo.log('img onclick');
1517         this.fireEvent('click', this, e);
1518     },
1519     /**
1520      * Sets the url of the image - used to update it
1521      * @param {String} url the url of the image
1522      */
1523     
1524     setSrc : function(url)
1525     {
1526         this.src =  url;
1527         
1528         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1529             this.el.dom.src =  url;
1530             return;
1531         }
1532         
1533         this.el.select('img', true).first().dom.src =  url;
1534     }
1535     
1536     
1537    
1538 });
1539
1540  /*
1541  * - LGPL
1542  *
1543  * image
1544  * 
1545  */
1546
1547
1548 /**
1549  * @class Roo.bootstrap.Link
1550  * @extends Roo.bootstrap.Component
1551  * Bootstrap Link Class
1552  * @cfg {String} alt image alternative text
1553  * @cfg {String} href a tag href
1554  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1555  * @cfg {String} html the content of the link.
1556  * @cfg {String} anchor name for the anchor link
1557  * @cfg {String} fa - favicon
1558
1559  * @cfg {Boolean} preventDefault (true | false) default false
1560
1561  * 
1562  * @constructor
1563  * Create a new Input
1564  * @param {Object} config The config object
1565  */
1566
1567 Roo.bootstrap.Link = function(config){
1568     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1569     
1570     this.addEvents({
1571         // img events
1572         /**
1573          * @event click
1574          * The img click event for the img.
1575          * @param {Roo.EventObject} e
1576          */
1577         "click" : true
1578     });
1579 };
1580
1581 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1582     
1583     href: false,
1584     target: false,
1585     preventDefault: false,
1586     anchor : false,
1587     alt : false,
1588     fa: false,
1589
1590
1591     getAutoCreate : function()
1592     {
1593         var html = this.html || '';
1594         
1595         if (this.fa !== false) {
1596             html = '<i class="fa fa-' + this.fa + '"></i>';
1597         }
1598         var cfg = {
1599             tag: 'a'
1600         };
1601         // anchor's do not require html/href...
1602         if (this.anchor === false) {
1603             cfg.html = html;
1604             cfg.href = this.href || '#';
1605         } else {
1606             cfg.name = this.anchor;
1607             if (this.html !== false || this.fa !== false) {
1608                 cfg.html = html;
1609             }
1610             if (this.href !== false) {
1611                 cfg.href = this.href;
1612             }
1613         }
1614         
1615         if(this.alt !== false){
1616             cfg.alt = this.alt;
1617         }
1618         
1619         
1620         if(this.target !== false) {
1621             cfg.target = this.target;
1622         }
1623         
1624         return cfg;
1625     },
1626     
1627     initEvents: function() {
1628         
1629         if(!this.href || this.preventDefault){
1630             this.el.on('click', this.onClick, this);
1631         }
1632     },
1633     
1634     onClick : function(e)
1635     {
1636         if(this.preventDefault){
1637             e.preventDefault();
1638         }
1639         //Roo.log('img onclick');
1640         this.fireEvent('click', this, e);
1641     }
1642    
1643 });
1644
1645  /*
1646  * - LGPL
1647  *
1648  * header
1649  * 
1650  */
1651
1652 /**
1653  * @class Roo.bootstrap.Header
1654  * @extends Roo.bootstrap.Component
1655  * Bootstrap Header class
1656  * @cfg {String} html content of header
1657  * @cfg {Number} level (1|2|3|4|5|6) default 1
1658  * 
1659  * @constructor
1660  * Create a new Header
1661  * @param {Object} config The config object
1662  */
1663
1664
1665 Roo.bootstrap.Header  = function(config){
1666     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1667 };
1668
1669 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1670     
1671     //href : false,
1672     html : false,
1673     level : 1,
1674     
1675     
1676     
1677     getAutoCreate : function(){
1678         
1679         
1680         
1681         var cfg = {
1682             tag: 'h' + (1 *this.level),
1683             html: this.html || ''
1684         } ;
1685         
1686         return cfg;
1687     }
1688    
1689 });
1690
1691  
1692
1693  /*
1694  * Based on:
1695  * Ext JS Library 1.1.1
1696  * Copyright(c) 2006-2007, Ext JS, LLC.
1697  *
1698  * Originally Released Under LGPL - original licence link has changed is not relivant.
1699  *
1700  * Fork - LGPL
1701  * <script type="text/javascript">
1702  */
1703  
1704 /**
1705  * @class Roo.bootstrap.MenuMgr
1706  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1707  * @singleton
1708  */
1709 Roo.bootstrap.MenuMgr = function(){
1710    var menus, active, groups = {}, attached = false, lastShow = new Date();
1711
1712    // private - called when first menu is created
1713    function init(){
1714        menus = {};
1715        active = new Roo.util.MixedCollection();
1716        Roo.get(document).addKeyListener(27, function(){
1717            if(active.length > 0){
1718                hideAll();
1719            }
1720        });
1721    }
1722
1723    // private
1724    function hideAll(){
1725        if(active && active.length > 0){
1726            var c = active.clone();
1727            c.each(function(m){
1728                m.hide();
1729            });
1730        }
1731    }
1732
1733    // private
1734    function onHide(m){
1735        active.remove(m);
1736        if(active.length < 1){
1737            Roo.get(document).un("mouseup", onMouseDown);
1738             
1739            attached = false;
1740        }
1741    }
1742
1743    // private
1744    function onShow(m){
1745        var last = active.last();
1746        lastShow = new Date();
1747        active.add(m);
1748        if(!attached){
1749           Roo.get(document).on("mouseup", onMouseDown);
1750            
1751            attached = true;
1752        }
1753        if(m.parentMenu){
1754           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1755           m.parentMenu.activeChild = m;
1756        }else if(last && last.isVisible()){
1757           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1758        }
1759    }
1760
1761    // private
1762    function onBeforeHide(m){
1763        if(m.activeChild){
1764            m.activeChild.hide();
1765        }
1766        if(m.autoHideTimer){
1767            clearTimeout(m.autoHideTimer);
1768            delete m.autoHideTimer;
1769        }
1770    }
1771
1772    // private
1773    function onBeforeShow(m){
1774        var pm = m.parentMenu;
1775        if(!pm && !m.allowOtherMenus){
1776            hideAll();
1777        }else if(pm && pm.activeChild && active != m){
1778            pm.activeChild.hide();
1779        }
1780    }
1781
1782    // private this should really trigger on mouseup..
1783    function onMouseDown(e){
1784         Roo.log("on Mouse Up");
1785         
1786         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1787             Roo.log("MenuManager hideAll");
1788             hideAll();
1789             e.stopEvent();
1790         }
1791         
1792         
1793    }
1794
1795    // private
1796    function onBeforeCheck(mi, state){
1797        if(state){
1798            var g = groups[mi.group];
1799            for(var i = 0, l = g.length; i < l; i++){
1800                if(g[i] != mi){
1801                    g[i].setChecked(false);
1802                }
1803            }
1804        }
1805    }
1806
1807    return {
1808
1809        /**
1810         * Hides all menus that are currently visible
1811         */
1812        hideAll : function(){
1813             hideAll();  
1814        },
1815
1816        // private
1817        register : function(menu){
1818            if(!menus){
1819                init();
1820            }
1821            menus[menu.id] = menu;
1822            menu.on("beforehide", onBeforeHide);
1823            menu.on("hide", onHide);
1824            menu.on("beforeshow", onBeforeShow);
1825            menu.on("show", onShow);
1826            var g = menu.group;
1827            if(g && menu.events["checkchange"]){
1828                if(!groups[g]){
1829                    groups[g] = [];
1830                }
1831                groups[g].push(menu);
1832                menu.on("checkchange", onCheck);
1833            }
1834        },
1835
1836         /**
1837          * Returns a {@link Roo.menu.Menu} object
1838          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1839          * be used to generate and return a new Menu instance.
1840          */
1841        get : function(menu){
1842            if(typeof menu == "string"){ // menu id
1843                return menus[menu];
1844            }else if(menu.events){  // menu instance
1845                return menu;
1846            }
1847            /*else if(typeof menu.length == 'number'){ // array of menu items?
1848                return new Roo.bootstrap.Menu({items:menu});
1849            }else{ // otherwise, must be a config
1850                return new Roo.bootstrap.Menu(menu);
1851            }
1852            */
1853            return false;
1854        },
1855
1856        // private
1857        unregister : function(menu){
1858            delete menus[menu.id];
1859            menu.un("beforehide", onBeforeHide);
1860            menu.un("hide", onHide);
1861            menu.un("beforeshow", onBeforeShow);
1862            menu.un("show", onShow);
1863            var g = menu.group;
1864            if(g && menu.events["checkchange"]){
1865                groups[g].remove(menu);
1866                menu.un("checkchange", onCheck);
1867            }
1868        },
1869
1870        // private
1871        registerCheckable : function(menuItem){
1872            var g = menuItem.group;
1873            if(g){
1874                if(!groups[g]){
1875                    groups[g] = [];
1876                }
1877                groups[g].push(menuItem);
1878                menuItem.on("beforecheckchange", onBeforeCheck);
1879            }
1880        },
1881
1882        // private
1883        unregisterCheckable : function(menuItem){
1884            var g = menuItem.group;
1885            if(g){
1886                groups[g].remove(menuItem);
1887                menuItem.un("beforecheckchange", onBeforeCheck);
1888            }
1889        }
1890    };
1891 }();/*
1892  * - LGPL
1893  *
1894  * menu
1895  * 
1896  */
1897
1898 /**
1899  * @class Roo.bootstrap.Menu
1900  * @extends Roo.bootstrap.Component
1901  * Bootstrap Menu class - container for MenuItems
1902  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1903  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1904  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1905  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1906  * 
1907  * @constructor
1908  * Create a new Menu
1909  * @param {Object} config The config object
1910  */
1911
1912
1913 Roo.bootstrap.Menu = function(config){
1914     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1915     if (this.registerMenu && this.type != 'treeview')  {
1916         Roo.bootstrap.MenuMgr.register(this);
1917     }
1918     this.addEvents({
1919         /**
1920          * @event beforeshow
1921          * Fires before this menu is displayed
1922          * @param {Roo.menu.Menu} this
1923          */
1924         beforeshow : true,
1925         /**
1926          * @event beforehide
1927          * Fires before this menu is hidden
1928          * @param {Roo.menu.Menu} this
1929          */
1930         beforehide : true,
1931         /**
1932          * @event show
1933          * Fires after this menu is displayed
1934          * @param {Roo.menu.Menu} this
1935          */
1936         show : true,
1937         /**
1938          * @event hide
1939          * Fires after this menu is hidden
1940          * @param {Roo.menu.Menu} this
1941          */
1942         hide : true,
1943         /**
1944          * @event click
1945          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1946          * @param {Roo.menu.Menu} this
1947          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1948          * @param {Roo.EventObject} e
1949          */
1950         click : true,
1951         /**
1952          * @event mouseover
1953          * Fires when the mouse is hovering over this menu
1954          * @param {Roo.menu.Menu} this
1955          * @param {Roo.EventObject} e
1956          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1957          */
1958         mouseover : true,
1959         /**
1960          * @event mouseout
1961          * Fires when the mouse exits this menu
1962          * @param {Roo.menu.Menu} this
1963          * @param {Roo.EventObject} e
1964          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1965          */
1966         mouseout : true,
1967         /**
1968          * @event itemclick
1969          * Fires when a menu item contained in this menu is clicked
1970          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1971          * @param {Roo.EventObject} e
1972          */
1973         itemclick: true
1974     });
1975     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1976 };
1977
1978 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1979     
1980    /// html : false,
1981     //align : '',
1982     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1983     type: false,
1984     /**
1985      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1986      */
1987     registerMenu : true,
1988     
1989     menuItems :false, // stores the menu items..
1990     
1991     hidden:true,
1992         
1993     parentMenu : false,
1994     
1995     stopEvent : true,
1996     
1997     isLink : false,
1998     
1999     getChildContainer : function() {
2000         return this.el;  
2001     },
2002     
2003     getAutoCreate : function(){
2004          
2005         //if (['right'].indexOf(this.align)!==-1) {
2006         //    cfg.cn[1].cls += ' pull-right'
2007         //}
2008         
2009         
2010         var cfg = {
2011             tag : 'ul',
2012             cls : 'dropdown-menu' ,
2013             style : 'z-index:1000'
2014             
2015         };
2016         
2017         if (this.type === 'submenu') {
2018             cfg.cls = 'submenu active';
2019         }
2020         if (this.type === 'treeview') {
2021             cfg.cls = 'treeview-menu';
2022         }
2023         
2024         return cfg;
2025     },
2026     initEvents : function() {
2027         
2028        // Roo.log("ADD event");
2029        // Roo.log(this.triggerEl.dom);
2030         
2031         this.triggerEl.on('click', this.onTriggerClick, this);
2032         
2033         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2034         
2035         this.triggerEl.addClass('dropdown-toggle');
2036         
2037         if (Roo.isTouch) {
2038             this.el.on('touchstart'  , this.onTouch, this);
2039         }
2040         this.el.on('click' , this.onClick, this);
2041
2042         this.el.on("mouseover", this.onMouseOver, this);
2043         this.el.on("mouseout", this.onMouseOut, this);
2044         
2045     },
2046     
2047     findTargetItem : function(e)
2048     {
2049         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2050         if(!t){
2051             return false;
2052         }
2053         //Roo.log(t);         Roo.log(t.id);
2054         if(t && t.id){
2055             //Roo.log(this.menuitems);
2056             return this.menuitems.get(t.id);
2057             
2058             //return this.items.get(t.menuItemId);
2059         }
2060         
2061         return false;
2062     },
2063     
2064     onTouch : function(e) 
2065     {
2066         Roo.log("menu.onTouch");
2067         //e.stopEvent(); this make the user popdown broken
2068         this.onClick(e);
2069     },
2070     
2071     onClick : function(e)
2072     {
2073         Roo.log("menu.onClick");
2074         
2075         var t = this.findTargetItem(e);
2076         if(!t || t.isContainer){
2077             return;
2078         }
2079         Roo.log(e);
2080         /*
2081         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2082             if(t == this.activeItem && t.shouldDeactivate(e)){
2083                 this.activeItem.deactivate();
2084                 delete this.activeItem;
2085                 return;
2086             }
2087             if(t.canActivate){
2088                 this.setActiveItem(t, true);
2089             }
2090             return;
2091             
2092             
2093         }
2094         */
2095        
2096         Roo.log('pass click event');
2097         
2098         t.onClick(e);
2099         
2100         this.fireEvent("click", this, t, e);
2101         
2102         var _this = this;
2103         
2104         (function() { _this.hide(); }).defer(100);
2105     },
2106     
2107     onMouseOver : function(e){
2108         var t  = this.findTargetItem(e);
2109         //Roo.log(t);
2110         //if(t){
2111         //    if(t.canActivate && !t.disabled){
2112         //        this.setActiveItem(t, true);
2113         //    }
2114         //}
2115         
2116         this.fireEvent("mouseover", this, e, t);
2117     },
2118     isVisible : function(){
2119         return !this.hidden;
2120     },
2121      onMouseOut : function(e){
2122         var t  = this.findTargetItem(e);
2123         
2124         //if(t ){
2125         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2126         //        this.activeItem.deactivate();
2127         //        delete this.activeItem;
2128         //    }
2129         //}
2130         this.fireEvent("mouseout", this, e, t);
2131     },
2132     
2133     
2134     /**
2135      * Displays this menu relative to another element
2136      * @param {String/HTMLElement/Roo.Element} element The element to align to
2137      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2138      * the element (defaults to this.defaultAlign)
2139      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2140      */
2141     show : function(el, pos, parentMenu){
2142         this.parentMenu = parentMenu;
2143         if(!this.el){
2144             this.render();
2145         }
2146         this.fireEvent("beforeshow", this);
2147         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2148     },
2149      /**
2150      * Displays this menu at a specific xy position
2151      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2152      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2153      */
2154     showAt : function(xy, parentMenu, /* private: */_e){
2155         this.parentMenu = parentMenu;
2156         if(!this.el){
2157             this.render();
2158         }
2159         if(_e !== false){
2160             this.fireEvent("beforeshow", this);
2161             //xy = this.el.adjustForConstraints(xy);
2162         }
2163         
2164         //this.el.show();
2165         this.hideMenuItems();
2166         this.hidden = false;
2167         this.triggerEl.addClass('open');
2168         
2169         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2170             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2171         }
2172         
2173         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2174             this.el.setXY(xy);
2175         }
2176         
2177         this.focus();
2178         this.fireEvent("show", this);
2179     },
2180     
2181     focus : function(){
2182         return;
2183         if(!this.hidden){
2184             this.doFocus.defer(50, this);
2185         }
2186     },
2187
2188     doFocus : function(){
2189         if(!this.hidden){
2190             this.focusEl.focus();
2191         }
2192     },
2193
2194     /**
2195      * Hides this menu and optionally all parent menus
2196      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2197      */
2198     hide : function(deep)
2199     {
2200         
2201         this.hideMenuItems();
2202         if(this.el && this.isVisible()){
2203             this.fireEvent("beforehide", this);
2204             if(this.activeItem){
2205                 this.activeItem.deactivate();
2206                 this.activeItem = null;
2207             }
2208             this.triggerEl.removeClass('open');;
2209             this.hidden = true;
2210             this.fireEvent("hide", this);
2211         }
2212         if(deep === true && this.parentMenu){
2213             this.parentMenu.hide(true);
2214         }
2215     },
2216     
2217     onTriggerClick : function(e)
2218     {
2219         Roo.log('trigger click');
2220         
2221         var target = e.getTarget();
2222         
2223         Roo.log(target.nodeName.toLowerCase());
2224         
2225         if(target.nodeName.toLowerCase() === 'i'){
2226             e.preventDefault();
2227         }
2228         
2229     },
2230     
2231     onTriggerPress  : function(e)
2232     {
2233         Roo.log('trigger press');
2234         //Roo.log(e.getTarget());
2235        // Roo.log(this.triggerEl.dom);
2236        
2237         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2238         var pel = Roo.get(e.getTarget());
2239         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2240             Roo.log('is treeview or dropdown?');
2241             return;
2242         }
2243         
2244         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2245             return;
2246         }
2247         
2248         if (this.isVisible()) {
2249             Roo.log('hide');
2250             this.hide();
2251         } else {
2252             Roo.log('show');
2253             this.show(this.triggerEl, false, false);
2254         }
2255         
2256         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2257             e.stopEvent();
2258         }
2259         
2260     },
2261        
2262     
2263     hideMenuItems : function()
2264     {
2265         Roo.log("hide Menu Items");
2266         if (!this.el) { 
2267             return;
2268         }
2269         //$(backdrop).remove()
2270         this.el.select('.open',true).each(function(aa) {
2271             
2272             aa.removeClass('open');
2273           //var parent = getParent($(this))
2274           //var relatedTarget = { relatedTarget: this }
2275           
2276            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2277           //if (e.isDefaultPrevented()) return
2278            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2279         });
2280     },
2281     addxtypeChild : function (tree, cntr) {
2282         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2283           
2284         this.menuitems.add(comp);
2285         return comp;
2286
2287     },
2288     getEl : function()
2289     {
2290         Roo.log(this.el);
2291         return this.el;
2292     }
2293 });
2294
2295  
2296  /*
2297  * - LGPL
2298  *
2299  * menu item
2300  * 
2301  */
2302
2303
2304 /**
2305  * @class Roo.bootstrap.MenuItem
2306  * @extends Roo.bootstrap.Component
2307  * Bootstrap MenuItem class
2308  * @cfg {String} html the menu label
2309  * @cfg {String} href the link
2310  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2311  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2312  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2313  * @cfg {String} fa favicon to show on left of menu item.
2314  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2315  * 
2316  * 
2317  * @constructor
2318  * Create a new MenuItem
2319  * @param {Object} config The config object
2320  */
2321
2322
2323 Roo.bootstrap.MenuItem = function(config){
2324     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2325     this.addEvents({
2326         // raw events
2327         /**
2328          * @event click
2329          * The raw click event for the entire grid.
2330          * @param {Roo.bootstrap.MenuItem} this
2331          * @param {Roo.EventObject} e
2332          */
2333         "click" : true
2334     });
2335 };
2336
2337 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2338     
2339     href : false,
2340     html : false,
2341     preventDefault: false,
2342     isContainer : false,
2343     active : false,
2344     fa: false,
2345     
2346     getAutoCreate : function(){
2347         
2348         if(this.isContainer){
2349             return {
2350                 tag: 'li',
2351                 cls: 'dropdown-menu-item'
2352             };
2353         }
2354         var ctag = {
2355             tag: 'span',
2356             html: 'Link'
2357         };
2358         
2359         var anc = {
2360             tag : 'a',
2361             href : '#',
2362             cn : [  ]
2363         };
2364         
2365         if (this.fa !== false) {
2366             anc.cn.push({
2367                 tag : 'i',
2368                 cls : 'fa fa-' + this.fa
2369             });
2370         }
2371         
2372         anc.cn.push(ctag);
2373         
2374         
2375         var cfg= {
2376             tag: 'li',
2377             cls: 'dropdown-menu-item',
2378             cn: [ anc ]
2379         };
2380         if (this.parent().type == 'treeview') {
2381             cfg.cls = 'treeview-menu';
2382         }
2383         if (this.active) {
2384             cfg.cls += ' active';
2385         }
2386         
2387         
2388         
2389         anc.href = this.href || cfg.cn[0].href ;
2390         ctag.html = this.html || cfg.cn[0].html ;
2391         return cfg;
2392     },
2393     
2394     initEvents: function()
2395     {
2396         if (this.parent().type == 'treeview') {
2397             this.el.select('a').on('click', this.onClick, this);
2398         }
2399         if (this.menu) {
2400             this.menu.parentType = this.xtype;
2401             this.menu.triggerEl = this.el;
2402             this.menu = this.addxtype(Roo.apply({}, this.menu));
2403         }
2404         
2405     },
2406     onClick : function(e)
2407     {
2408         Roo.log('item on click ');
2409         
2410         if(this.preventDefault){
2411             e.preventDefault();
2412         }
2413         //this.parent().hideMenuItems();
2414         
2415         this.fireEvent('click', this, e);
2416     },
2417     getEl : function()
2418     {
2419         return this.el;
2420     } 
2421 });
2422
2423  
2424
2425  /*
2426  * - LGPL
2427  *
2428  * menu separator
2429  * 
2430  */
2431
2432
2433 /**
2434  * @class Roo.bootstrap.MenuSeparator
2435  * @extends Roo.bootstrap.Component
2436  * Bootstrap MenuSeparator class
2437  * 
2438  * @constructor
2439  * Create a new MenuItem
2440  * @param {Object} config The config object
2441  */
2442
2443
2444 Roo.bootstrap.MenuSeparator = function(config){
2445     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2446 };
2447
2448 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2449     
2450     getAutoCreate : function(){
2451         var cfg = {
2452             cls: 'divider',
2453             tag : 'li'
2454         };
2455         
2456         return cfg;
2457     }
2458    
2459 });
2460
2461  
2462
2463  
2464 /*
2465 * Licence: LGPL
2466 */
2467
2468 /**
2469  * @class Roo.bootstrap.Modal
2470  * @extends Roo.bootstrap.Component
2471  * Bootstrap Modal class
2472  * @cfg {String} title Title of dialog
2473  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2474  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2475  * @cfg {Boolean} specificTitle default false
2476  * @cfg {Array} buttons Array of buttons or standard button set..
2477  * @cfg {String} buttonPosition (left|right|center) default right
2478  * @cfg {Boolean} animate default true
2479  * @cfg {Boolean} allow_close default true
2480  * @cfg {Boolean} fitwindow default false
2481  * @cfg {String} size (sm|lg) default empty
2482  *
2483  *
2484  * @constructor
2485  * Create a new Modal Dialog
2486  * @param {Object} config The config object
2487  */
2488
2489 Roo.bootstrap.Modal = function(config){
2490     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2491     this.addEvents({
2492         // raw events
2493         /**
2494          * @event btnclick
2495          * The raw btnclick event for the button
2496          * @param {Roo.EventObject} e
2497          */
2498         "btnclick" : true
2499     });
2500     this.buttons = this.buttons || [];
2501
2502     if (this.tmpl) {
2503         this.tmpl = Roo.factory(this.tmpl);
2504     }
2505
2506 };
2507
2508 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2509
2510     title : 'test dialog',
2511
2512     buttons : false,
2513
2514     // set on load...
2515
2516     html: false,
2517
2518     tmp: false,
2519
2520     specificTitle: false,
2521
2522     buttonPosition: 'right',
2523
2524     allow_close : true,
2525
2526     animate : true,
2527
2528     fitwindow: false,
2529
2530
2531      // private
2532     dialogEl: false,
2533     bodyEl:  false,
2534     footerEl:  false,
2535     titleEl:  false,
2536     closeEl:  false,
2537
2538     size: '',
2539
2540
2541     onRender : function(ct, position)
2542     {
2543         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2544
2545         if(!this.el){
2546             var cfg = Roo.apply({},  this.getAutoCreate());
2547             cfg.id = Roo.id();
2548             //if(!cfg.name){
2549             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2550             //}
2551             //if (!cfg.name.length) {
2552             //    delete cfg.name;
2553            // }
2554             if (this.cls) {
2555                 cfg.cls += ' ' + this.cls;
2556             }
2557             if (this.style) {
2558                 cfg.style = this.style;
2559             }
2560             this.el = Roo.get(document.body).createChild(cfg, position);
2561         }
2562         //var type = this.el.dom.type;
2563
2564
2565         if(this.tabIndex !== undefined){
2566             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2567         }
2568
2569         this.dialogEl = this.el.select('.modal-dialog',true).first();
2570         this.bodyEl = this.el.select('.modal-body',true).first();
2571         this.closeEl = this.el.select('.modal-header .close', true).first();
2572         this.headerEl = this.el.select('.modal-header',true).first();
2573         this.titleEl = this.el.select('.modal-title',true).first();
2574         this.footerEl = this.el.select('.modal-footer',true).first();
2575
2576         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2577         this.maskEl.enableDisplayMode("block");
2578         this.maskEl.hide();
2579         //this.el.addClass("x-dlg-modal");
2580
2581         if (this.buttons.length) {
2582             Roo.each(this.buttons, function(bb) {
2583                 var b = Roo.apply({}, bb);
2584                 b.xns = b.xns || Roo.bootstrap;
2585                 b.xtype = b.xtype || 'Button';
2586                 if (typeof(b.listeners) == 'undefined') {
2587                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2588                 }
2589
2590                 var btn = Roo.factory(b);
2591
2592                 btn.render(this.el.select('.modal-footer div').first());
2593
2594             },this);
2595         }
2596         // render the children.
2597         var nitems = [];
2598
2599         if(typeof(this.items) != 'undefined'){
2600             var items = this.items;
2601             delete this.items;
2602
2603             for(var i =0;i < items.length;i++) {
2604                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2605             }
2606         }
2607
2608         this.items = nitems;
2609
2610         // where are these used - they used to be body/close/footer
2611
2612
2613         this.initEvents();
2614         //this.el.addClass([this.fieldClass, this.cls]);
2615
2616     },
2617
2618     getAutoCreate : function(){
2619
2620
2621         var bdy = {
2622                 cls : 'modal-body',
2623                 html : this.html || ''
2624         };
2625
2626         var title = {
2627             tag: 'h4',
2628             cls : 'modal-title',
2629             html : this.title
2630         };
2631
2632         if(this.specificTitle){
2633             title = this.title;
2634
2635         };
2636
2637         var header = [];
2638         if (this.allow_close) {
2639             header.push({
2640                 tag: 'button',
2641                 cls : 'close',
2642                 html : '&times'
2643             });
2644         }
2645
2646         header.push(title);
2647
2648         var size = '';
2649
2650         if(this.size.length){
2651             size = 'modal-' + this.size;
2652         }
2653
2654         var modal = {
2655             cls: "modal",
2656             style : 'display: none',
2657             cn : [
2658                 {
2659                     cls: "modal-dialog " + size,
2660                     cn : [
2661                         {
2662                             cls : "modal-content",
2663                             cn : [
2664                                 {
2665                                     cls : 'modal-header',
2666                                     cn : header
2667                                 },
2668                                 bdy,
2669                                 {
2670                                     cls : 'modal-footer',
2671                                     cn : [
2672                                         {
2673                                             tag: 'div',
2674                                             cls: 'btn-' + this.buttonPosition
2675                                         }
2676                                     ]
2677
2678                                 }
2679
2680
2681                             ]
2682
2683                         }
2684                     ]
2685
2686                 }
2687             ]
2688         };
2689
2690         if(this.animate){
2691             modal.cls += ' fade';
2692         }
2693
2694         return modal;
2695
2696     },
2697     getChildContainer : function() {
2698
2699          return this.bodyEl;
2700
2701     },
2702     getButtonContainer : function() {
2703          return this.el.select('.modal-footer div',true).first();
2704
2705     },
2706     initEvents : function()
2707     {
2708         if (this.allow_close) {
2709             this.closeEl.on('click', this.hide, this);
2710         }
2711         Roo.EventManager.onWindowResize(this.resize, this, true);
2712
2713
2714     },
2715
2716     resize : function()
2717     {
2718         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2719         if (this.fitwindow) {
2720             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2721             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2722             this.setSize(w,h);
2723         }
2724     },
2725
2726     setSize : function(w,h)
2727     {
2728         if (!w && !h) {
2729             return;
2730         }
2731         this.resizeTo(w,h);
2732     },
2733
2734     show : function() {
2735
2736         if (!this.rendered) {
2737             this.render();
2738         }
2739
2740         this.el.setStyle('display', 'block');
2741
2742         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2743             var _this = this;
2744             (function(){
2745                 this.el.addClass('in');
2746             }).defer(50, this);
2747         }else{
2748             this.el.addClass('in');
2749
2750         }
2751
2752         // not sure how we can show data in here..
2753         //if (this.tmpl) {
2754         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2755         //}
2756
2757         Roo.get(document.body).addClass("x-body-masked");
2758         
2759         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2760         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2761         this.maskEl.show();
2762         
2763         this.resize();
2764         
2765         this.fireEvent('show', this);
2766
2767         // set zindex here - otherwise it appears to be ignored...
2768         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2769
2770         (function () {
2771             this.items.forEach( function(e) {
2772                 e.layout ? e.layout() : false;
2773
2774             });
2775         }).defer(100,this);
2776
2777     },
2778     hide : function()
2779     {
2780         if(this.fireEvent("beforehide", this) !== false){
2781             this.maskEl.hide();
2782             Roo.get(document.body).removeClass("x-body-masked");
2783             this.el.removeClass('in');
2784             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2785
2786             if(this.animate){ // why
2787                 var _this = this;
2788                 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2789             }else{
2790                 this.el.setStyle('display', 'none');
2791             }
2792             this.fireEvent('hide', this);
2793         }
2794     },
2795
2796     addButton : function(str, cb)
2797     {
2798
2799
2800         var b = Roo.apply({}, { html : str } );
2801         b.xns = b.xns || Roo.bootstrap;
2802         b.xtype = b.xtype || 'Button';
2803         if (typeof(b.listeners) == 'undefined') {
2804             b.listeners = { click : cb.createDelegate(this)  };
2805         }
2806
2807         var btn = Roo.factory(b);
2808
2809         btn.render(this.el.select('.modal-footer div').first());
2810
2811         return btn;
2812
2813     },
2814
2815     setDefaultButton : function(btn)
2816     {
2817         //this.el.select('.modal-footer').()
2818     },
2819     diff : false,
2820
2821     resizeTo: function(w,h)
2822     {
2823         // skip.. ?? why??
2824
2825         this.dialogEl.setWidth(w);
2826         if (this.diff === false) {
2827             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2828         }
2829
2830         this.bodyEl.setHeight(h-this.diff);
2831
2832
2833     },
2834     setContentSize  : function(w, h)
2835     {
2836
2837     },
2838     onButtonClick: function(btn,e)
2839     {
2840         //Roo.log([a,b,c]);
2841         this.fireEvent('btnclick', btn.name, e);
2842     },
2843      /**
2844      * Set the title of the Dialog
2845      * @param {String} str new Title
2846      */
2847     setTitle: function(str) {
2848         this.titleEl.dom.innerHTML = str;
2849     },
2850     /**
2851      * Set the body of the Dialog
2852      * @param {String} str new Title
2853      */
2854     setBody: function(str) {
2855         this.bodyEl.dom.innerHTML = str;
2856     },
2857     /**
2858      * Set the body of the Dialog using the template
2859      * @param {Obj} data - apply this data to the template and replace the body contents.
2860      */
2861     applyBody: function(obj)
2862     {
2863         if (!this.tmpl) {
2864             Roo.log("Error - using apply Body without a template");
2865             //code
2866         }
2867         this.tmpl.overwrite(this.bodyEl, obj);
2868     }
2869
2870 });
2871
2872
2873 Roo.apply(Roo.bootstrap.Modal,  {
2874     /**
2875          * Button config that displays a single OK button
2876          * @type Object
2877          */
2878         OK :  [{
2879             name : 'ok',
2880             weight : 'primary',
2881             html : 'OK'
2882         }],
2883         /**
2884          * Button config that displays Yes and No buttons
2885          * @type Object
2886          */
2887         YESNO : [
2888             {
2889                 name  : 'no',
2890                 html : 'No'
2891             },
2892             {
2893                 name  :'yes',
2894                 weight : 'primary',
2895                 html : 'Yes'
2896             }
2897         ],
2898
2899         /**
2900          * Button config that displays OK and Cancel buttons
2901          * @type Object
2902          */
2903         OKCANCEL : [
2904             {
2905                name : 'cancel',
2906                 html : 'Cancel'
2907             },
2908             {
2909                 name : 'ok',
2910                 weight : 'primary',
2911                 html : 'OK'
2912             }
2913         ],
2914         /**
2915          * Button config that displays Yes, No and Cancel buttons
2916          * @type Object
2917          */
2918         YESNOCANCEL : [
2919             {
2920                 name : 'yes',
2921                 weight : 'primary',
2922                 html : 'Yes'
2923             },
2924             {
2925                 name : 'no',
2926                 html : 'No'
2927             },
2928             {
2929                 name : 'cancel',
2930                 html : 'Cancel'
2931             }
2932         ],
2933         
2934         zIndex : 10001
2935 });
2936 /*
2937  * - LGPL
2938  *
2939  * messagebox - can be used as a replace
2940  * 
2941  */
2942 /**
2943  * @class Roo.MessageBox
2944  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2945  * Example usage:
2946  *<pre><code>
2947 // Basic alert:
2948 Roo.Msg.alert('Status', 'Changes saved successfully.');
2949
2950 // Prompt for user data:
2951 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2952     if (btn == 'ok'){
2953         // process text value...
2954     }
2955 });
2956
2957 // Show a dialog using config options:
2958 Roo.Msg.show({
2959    title:'Save Changes?',
2960    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2961    buttons: Roo.Msg.YESNOCANCEL,
2962    fn: processResult,
2963    animEl: 'elId'
2964 });
2965 </code></pre>
2966  * @singleton
2967  */
2968 Roo.bootstrap.MessageBox = function(){
2969     var dlg, opt, mask, waitTimer;
2970     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2971     var buttons, activeTextEl, bwidth;
2972
2973     
2974     // private
2975     var handleButton = function(button){
2976         dlg.hide();
2977         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2978     };
2979
2980     // private
2981     var handleHide = function(){
2982         if(opt && opt.cls){
2983             dlg.el.removeClass(opt.cls);
2984         }
2985         //if(waitTimer){
2986         //    Roo.TaskMgr.stop(waitTimer);
2987         //    waitTimer = null;
2988         //}
2989     };
2990
2991     // private
2992     var updateButtons = function(b){
2993         var width = 0;
2994         if(!b){
2995             buttons["ok"].hide();
2996             buttons["cancel"].hide();
2997             buttons["yes"].hide();
2998             buttons["no"].hide();
2999             //dlg.footer.dom.style.display = 'none';
3000             return width;
3001         }
3002         dlg.footerEl.dom.style.display = '';
3003         for(var k in buttons){
3004             if(typeof buttons[k] != "function"){
3005                 if(b[k]){
3006                     buttons[k].show();
3007                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3008                     width += buttons[k].el.getWidth()+15;
3009                 }else{
3010                     buttons[k].hide();
3011                 }
3012             }
3013         }
3014         return width;
3015     };
3016
3017     // private
3018     var handleEsc = function(d, k, e){
3019         if(opt && opt.closable !== false){
3020             dlg.hide();
3021         }
3022         if(e){
3023             e.stopEvent();
3024         }
3025     };
3026
3027     return {
3028         /**
3029          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3030          * @return {Roo.BasicDialog} The BasicDialog element
3031          */
3032         getDialog : function(){
3033            if(!dlg){
3034                 dlg = new Roo.bootstrap.Modal( {
3035                     //draggable: true,
3036                     //resizable:false,
3037                     //constraintoviewport:false,
3038                     //fixedcenter:true,
3039                     //collapsible : false,
3040                     //shim:true,
3041                     //modal: true,
3042                 //    width: 'auto',
3043                   //  height:100,
3044                     //buttonAlign:"center",
3045                     closeClick : function(){
3046                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3047                             handleButton("no");
3048                         }else{
3049                             handleButton("cancel");
3050                         }
3051                     }
3052                 });
3053                 dlg.render();
3054                 dlg.on("hide", handleHide);
3055                 mask = dlg.mask;
3056                 //dlg.addKeyListener(27, handleEsc);
3057                 buttons = {};
3058                 this.buttons = buttons;
3059                 var bt = this.buttonText;
3060                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3061                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3062                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3063                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3064                 //Roo.log(buttons);
3065                 bodyEl = dlg.bodyEl.createChild({
3066
3067                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3068                         '<textarea class="roo-mb-textarea"></textarea>' +
3069                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3070                 });
3071                 msgEl = bodyEl.dom.firstChild;
3072                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3073                 textboxEl.enableDisplayMode();
3074                 textboxEl.addKeyListener([10,13], function(){
3075                     if(dlg.isVisible() && opt && opt.buttons){
3076                         if(opt.buttons.ok){
3077                             handleButton("ok");
3078                         }else if(opt.buttons.yes){
3079                             handleButton("yes");
3080                         }
3081                     }
3082                 });
3083                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3084                 textareaEl.enableDisplayMode();
3085                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3086                 progressEl.enableDisplayMode();
3087                 
3088                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3089                 //var pf = progressEl.dom.firstChild;
3090                 //if (pf) {
3091                     //pp = Roo.get(pf.firstChild);
3092                     //pp.setHeight(pf.offsetHeight);
3093                 //}
3094                 
3095             }
3096             return dlg;
3097         },
3098
3099         /**
3100          * Updates the message box body text
3101          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3102          * the XHTML-compliant non-breaking space character '&amp;#160;')
3103          * @return {Roo.MessageBox} This message box
3104          */
3105         updateText : function(text)
3106         {
3107             if(!dlg.isVisible() && !opt.width){
3108                 dlg.dialogEl.setWidth(this.maxWidth);
3109                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3110             }
3111             msgEl.innerHTML = text || '&#160;';
3112       
3113             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3114             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3115             var w = Math.max(
3116                     Math.min(opt.width || cw , this.maxWidth), 
3117                     Math.max(opt.minWidth || this.minWidth, bwidth)
3118             );
3119             if(opt.prompt){
3120                 activeTextEl.setWidth(w);
3121             }
3122             if(dlg.isVisible()){
3123                 dlg.fixedcenter = false;
3124             }
3125             // to big, make it scroll. = But as usual stupid IE does not support
3126             // !important..
3127             
3128             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3129                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3130                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3131             } else {
3132                 bodyEl.dom.style.height = '';
3133                 bodyEl.dom.style.overflowY = '';
3134             }
3135             if (cw > w) {
3136                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3137             } else {
3138                 bodyEl.dom.style.overflowX = '';
3139             }
3140             
3141             dlg.setContentSize(w, bodyEl.getHeight());
3142             if(dlg.isVisible()){
3143                 dlg.fixedcenter = true;
3144             }
3145             return this;
3146         },
3147
3148         /**
3149          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3150          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3151          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3152          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3153          * @return {Roo.MessageBox} This message box
3154          */
3155         updateProgress : function(value, text){
3156             if(text){
3157                 this.updateText(text);
3158             }
3159             if (pp) { // weird bug on my firefox - for some reason this is not defined
3160                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3161             }
3162             return this;
3163         },        
3164
3165         /**
3166          * Returns true if the message box is currently displayed
3167          * @return {Boolean} True if the message box is visible, else false
3168          */
3169         isVisible : function(){
3170             return dlg && dlg.isVisible();  
3171         },
3172
3173         /**
3174          * Hides the message box if it is displayed
3175          */
3176         hide : function(){
3177             if(this.isVisible()){
3178                 dlg.hide();
3179             }  
3180         },
3181
3182         /**
3183          * Displays a new message box, or reinitializes an existing message box, based on the config options
3184          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3185          * The following config object properties are supported:
3186          * <pre>
3187 Property    Type             Description
3188 ----------  ---------------  ------------------------------------------------------------------------------------
3189 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3190                                    closes (defaults to undefined)
3191 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3192                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3193 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3194                                    progress and wait dialogs will ignore this property and always hide the
3195                                    close button as they can only be closed programmatically.
3196 cls               String           A custom CSS class to apply to the message box element
3197 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3198                                    displayed (defaults to 75)
3199 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3200                                    function will be btn (the name of the button that was clicked, if applicable,
3201                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3202                                    Progress and wait dialogs will ignore this option since they do not respond to
3203                                    user actions and can only be closed programmatically, so any required function
3204                                    should be called by the same code after it closes the dialog.
3205 icon              String           A CSS class that provides a background image to be used as an icon for
3206                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3207 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3208 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3209 modal             Boolean          False to allow user interaction with the page while the message box is
3210                                    displayed (defaults to true)
3211 msg               String           A string that will replace the existing message box body text (defaults
3212                                    to the XHTML-compliant non-breaking space character '&#160;')
3213 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3214 progress          Boolean          True to display a progress bar (defaults to false)
3215 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3216 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3217 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3218 title             String           The title text
3219 value             String           The string value to set into the active textbox element if displayed
3220 wait              Boolean          True to display a progress bar (defaults to false)
3221 width             Number           The width of the dialog in pixels
3222 </pre>
3223          *
3224          * Example usage:
3225          * <pre><code>
3226 Roo.Msg.show({
3227    title: 'Address',
3228    msg: 'Please enter your address:',
3229    width: 300,
3230    buttons: Roo.MessageBox.OKCANCEL,
3231    multiline: true,
3232    fn: saveAddress,
3233    animEl: 'addAddressBtn'
3234 });
3235 </code></pre>
3236          * @param {Object} config Configuration options
3237          * @return {Roo.MessageBox} This message box
3238          */
3239         show : function(options)
3240         {
3241             
3242             // this causes nightmares if you show one dialog after another
3243             // especially on callbacks..
3244              
3245             if(this.isVisible()){
3246                 
3247                 this.hide();
3248                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3249                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3250                 Roo.log("New Dialog Message:" +  options.msg )
3251                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3252                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3253                 
3254             }
3255             var d = this.getDialog();
3256             opt = options;
3257             d.setTitle(opt.title || "&#160;");
3258             d.closeEl.setDisplayed(opt.closable !== false);
3259             activeTextEl = textboxEl;
3260             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3261             if(opt.prompt){
3262                 if(opt.multiline){
3263                     textboxEl.hide();
3264                     textareaEl.show();
3265                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3266                         opt.multiline : this.defaultTextHeight);
3267                     activeTextEl = textareaEl;
3268                 }else{
3269                     textboxEl.show();
3270                     textareaEl.hide();
3271                 }
3272             }else{
3273                 textboxEl.hide();
3274                 textareaEl.hide();
3275             }
3276             progressEl.setDisplayed(opt.progress === true);
3277             this.updateProgress(0);
3278             activeTextEl.dom.value = opt.value || "";
3279             if(opt.prompt){
3280                 dlg.setDefaultButton(activeTextEl);
3281             }else{
3282                 var bs = opt.buttons;
3283                 var db = null;
3284                 if(bs && bs.ok){
3285                     db = buttons["ok"];
3286                 }else if(bs && bs.yes){
3287                     db = buttons["yes"];
3288                 }
3289                 dlg.setDefaultButton(db);
3290             }
3291             bwidth = updateButtons(opt.buttons);
3292             this.updateText(opt.msg);
3293             if(opt.cls){
3294                 d.el.addClass(opt.cls);
3295             }
3296             d.proxyDrag = opt.proxyDrag === true;
3297             d.modal = opt.modal !== false;
3298             d.mask = opt.modal !== false ? mask : false;
3299             if(!d.isVisible()){
3300                 // force it to the end of the z-index stack so it gets a cursor in FF
3301                 document.body.appendChild(dlg.el.dom);
3302                 d.animateTarget = null;
3303                 d.show(options.animEl);
3304             }
3305             return this;
3306         },
3307
3308         /**
3309          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3310          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3311          * and closing the message box when the process is complete.
3312          * @param {String} title The title bar text
3313          * @param {String} msg The message box body text
3314          * @return {Roo.MessageBox} This message box
3315          */
3316         progress : function(title, msg){
3317             this.show({
3318                 title : title,
3319                 msg : msg,
3320                 buttons: false,
3321                 progress:true,
3322                 closable:false,
3323                 minWidth: this.minProgressWidth,
3324                 modal : true
3325             });
3326             return this;
3327         },
3328
3329         /**
3330          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3331          * If a callback function is passed it will be called after the user clicks the button, and the
3332          * id of the button that was clicked will be passed as the only parameter to the callback
3333          * (could also be the top-right close button).
3334          * @param {String} title The title bar text
3335          * @param {String} msg The message box body text
3336          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3337          * @param {Object} scope (optional) The scope of the callback function
3338          * @return {Roo.MessageBox} This message box
3339          */
3340         alert : function(title, msg, fn, scope)
3341         {
3342             this.show({
3343                 title : title,
3344                 msg : msg,
3345                 buttons: this.OK,
3346                 fn: fn,
3347                 closable : false,
3348                 scope : scope,
3349                 modal : true
3350             });
3351             return this;
3352         },
3353
3354         /**
3355          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3356          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3357          * You are responsible for closing the message box when the process is complete.
3358          * @param {String} msg The message box body text
3359          * @param {String} title (optional) The title bar text
3360          * @return {Roo.MessageBox} This message box
3361          */
3362         wait : function(msg, title){
3363             this.show({
3364                 title : title,
3365                 msg : msg,
3366                 buttons: false,
3367                 closable:false,
3368                 progress:true,
3369                 modal:true,
3370                 width:300,
3371                 wait:true
3372             });
3373             waitTimer = Roo.TaskMgr.start({
3374                 run: function(i){
3375                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3376                 },
3377                 interval: 1000
3378             });
3379             return this;
3380         },
3381
3382         /**
3383          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3384          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3385          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3386          * @param {String} title The title bar text
3387          * @param {String} msg The message box body text
3388          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3389          * @param {Object} scope (optional) The scope of the callback function
3390          * @return {Roo.MessageBox} This message box
3391          */
3392         confirm : function(title, msg, fn, scope){
3393             this.show({
3394                 title : title,
3395                 msg : msg,
3396                 buttons: this.YESNO,
3397                 fn: fn,
3398                 scope : scope,
3399                 modal : true
3400             });
3401             return this;
3402         },
3403
3404         /**
3405          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3406          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3407          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3408          * (could also be the top-right close button) and the text that was entered will be passed as the two
3409          * parameters to the callback.
3410          * @param {String} title The title bar text
3411          * @param {String} msg The message box body text
3412          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3413          * @param {Object} scope (optional) The scope of the callback function
3414          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3415          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3416          * @return {Roo.MessageBox} This message box
3417          */
3418         prompt : function(title, msg, fn, scope, multiline){
3419             this.show({
3420                 title : title,
3421                 msg : msg,
3422                 buttons: this.OKCANCEL,
3423                 fn: fn,
3424                 minWidth:250,
3425                 scope : scope,
3426                 prompt:true,
3427                 multiline: multiline,
3428                 modal : true
3429             });
3430             return this;
3431         },
3432
3433         /**
3434          * Button config that displays a single OK button
3435          * @type Object
3436          */
3437         OK : {ok:true},
3438         /**
3439          * Button config that displays Yes and No buttons
3440          * @type Object
3441          */
3442         YESNO : {yes:true, no:true},
3443         /**
3444          * Button config that displays OK and Cancel buttons
3445          * @type Object
3446          */
3447         OKCANCEL : {ok:true, cancel:true},
3448         /**
3449          * Button config that displays Yes, No and Cancel buttons
3450          * @type Object
3451          */
3452         YESNOCANCEL : {yes:true, no:true, cancel:true},
3453
3454         /**
3455          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3456          * @type Number
3457          */
3458         defaultTextHeight : 75,
3459         /**
3460          * The maximum width in pixels of the message box (defaults to 600)
3461          * @type Number
3462          */
3463         maxWidth : 600,
3464         /**
3465          * The minimum width in pixels of the message box (defaults to 100)
3466          * @type Number
3467          */
3468         minWidth : 100,
3469         /**
3470          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3471          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3472          * @type Number
3473          */
3474         minProgressWidth : 250,
3475         /**
3476          * An object containing the default button text strings that can be overriden for localized language support.
3477          * Supported properties are: ok, cancel, yes and no.
3478          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3479          * @type Object
3480          */
3481         buttonText : {
3482             ok : "OK",
3483             cancel : "Cancel",
3484             yes : "Yes",
3485             no : "No"
3486         }
3487     };
3488 }();
3489
3490 /**
3491  * Shorthand for {@link Roo.MessageBox}
3492  */
3493 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3494 Roo.Msg = Roo.Msg || Roo.MessageBox;
3495 /*
3496  * - LGPL
3497  *
3498  * navbar
3499  * 
3500  */
3501
3502 /**
3503  * @class Roo.bootstrap.Navbar
3504  * @extends Roo.bootstrap.Component
3505  * Bootstrap Navbar class
3506
3507  * @constructor
3508  * Create a new Navbar
3509  * @param {Object} config The config object
3510  */
3511
3512
3513 Roo.bootstrap.Navbar = function(config){
3514     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3515     this.addEvents({
3516         // raw events
3517         /**
3518          * @event beforetoggle
3519          * Fire before toggle the menu
3520          * @param {Roo.EventObject} e
3521          */
3522         "beforetoggle" : true
3523     });
3524 };
3525
3526 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3527     
3528     
3529    
3530     // private
3531     navItems : false,
3532     loadMask : false,
3533     
3534     
3535     getAutoCreate : function(){
3536         
3537         
3538         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3539         
3540     },
3541     
3542     initEvents :function ()
3543     {
3544         //Roo.log(this.el.select('.navbar-toggle',true));
3545         this.el.select('.navbar-toggle',true).on('click', function() {
3546             if(this.fireEvent('beforetoggle', this) !== false){
3547                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3548             }
3549             
3550         }, this);
3551         
3552         var mark = {
3553             tag: "div",
3554             cls:"x-dlg-mask"
3555         };
3556         
3557         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3558         
3559         var size = this.el.getSize();
3560         this.maskEl.setSize(size.width, size.height);
3561         this.maskEl.enableDisplayMode("block");
3562         this.maskEl.hide();
3563         
3564         if(this.loadMask){
3565             this.maskEl.show();
3566         }
3567     },
3568     
3569     
3570     getChildContainer : function()
3571     {
3572         if (this.el.select('.collapse').getCount()) {
3573             return this.el.select('.collapse',true).first();
3574         }
3575         
3576         return this.el;
3577     },
3578     
3579     mask : function()
3580     {
3581         this.maskEl.show();
3582     },
3583     
3584     unmask : function()
3585     {
3586         this.maskEl.hide();
3587     } 
3588     
3589     
3590     
3591     
3592 });
3593
3594
3595
3596  
3597
3598  /*
3599  * - LGPL
3600  *
3601  * navbar
3602  * 
3603  */
3604
3605 /**
3606  * @class Roo.bootstrap.NavSimplebar
3607  * @extends Roo.bootstrap.Navbar
3608  * Bootstrap Sidebar class
3609  *
3610  * @cfg {Boolean} inverse is inverted color
3611  * 
3612  * @cfg {String} type (nav | pills | tabs)
3613  * @cfg {Boolean} arrangement stacked | justified
3614  * @cfg {String} align (left | right) alignment
3615  * 
3616  * @cfg {Boolean} main (true|false) main nav bar? default false
3617  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3618  * 
3619  * @cfg {String} tag (header|footer|nav|div) default is nav 
3620
3621  * 
3622  * 
3623  * 
3624  * @constructor
3625  * Create a new Sidebar
3626  * @param {Object} config The config object
3627  */
3628
3629
3630 Roo.bootstrap.NavSimplebar = function(config){
3631     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3632 };
3633
3634 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3635     
3636     inverse: false,
3637     
3638     type: false,
3639     arrangement: '',
3640     align : false,
3641     
3642     
3643     
3644     main : false,
3645     
3646     
3647     tag : false,
3648     
3649     
3650     getAutoCreate : function(){
3651         
3652         
3653         var cfg = {
3654             tag : this.tag || 'div',
3655             cls : 'navbar'
3656         };
3657           
3658         
3659         cfg.cn = [
3660             {
3661                 cls: 'nav',
3662                 tag : 'ul'
3663             }
3664         ];
3665         
3666          
3667         this.type = this.type || 'nav';
3668         if (['tabs','pills'].indexOf(this.type)!==-1) {
3669             cfg.cn[0].cls += ' nav-' + this.type
3670         
3671         
3672         } else {
3673             if (this.type!=='nav') {
3674                 Roo.log('nav type must be nav/tabs/pills')
3675             }
3676             cfg.cn[0].cls += ' navbar-nav'
3677         }
3678         
3679         
3680         
3681         
3682         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3683             cfg.cn[0].cls += ' nav-' + this.arrangement;
3684         }
3685         
3686         
3687         if (this.align === 'right') {
3688             cfg.cn[0].cls += ' navbar-right';
3689         }
3690         
3691         if (this.inverse) {
3692             cfg.cls += ' navbar-inverse';
3693             
3694         }
3695         
3696         
3697         return cfg;
3698     
3699         
3700     }
3701     
3702     
3703     
3704 });
3705
3706
3707
3708  
3709
3710  
3711        /*
3712  * - LGPL
3713  *
3714  * navbar
3715  * 
3716  */
3717
3718 /**
3719  * @class Roo.bootstrap.NavHeaderbar
3720  * @extends Roo.bootstrap.NavSimplebar
3721  * Bootstrap Sidebar class
3722  *
3723  * @cfg {String} brand what is brand
3724  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3725  * @cfg {String} brand_href href of the brand
3726  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3727  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3728  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3729  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3730  * 
3731  * @constructor
3732  * Create a new Sidebar
3733  * @param {Object} config The config object
3734  */
3735
3736
3737 Roo.bootstrap.NavHeaderbar = function(config){
3738     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3739       
3740 };
3741
3742 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3743     
3744     position: '',
3745     brand: '',
3746     brand_href: false,
3747     srButton : true,
3748     autohide : false,
3749     desktopCenter : false,
3750    
3751     
3752     getAutoCreate : function(){
3753         
3754         var   cfg = {
3755             tag: this.nav || 'nav',
3756             cls: 'navbar',
3757             role: 'navigation',
3758             cn: []
3759         };
3760         
3761         var cn = cfg.cn;
3762         if (this.desktopCenter) {
3763             cn.push({cls : 'container', cn : []});
3764             cn = cn[0].cn;
3765         }
3766         
3767         if(this.srButton){
3768             cn.push({
3769                 tag: 'div',
3770                 cls: 'navbar-header',
3771                 cn: [
3772                     {
3773                         tag: 'button',
3774                         type: 'button',
3775                         cls: 'navbar-toggle',
3776                         'data-toggle': 'collapse',
3777                         cn: [
3778                             {
3779                                 tag: 'span',
3780                                 cls: 'sr-only',
3781                                 html: 'Toggle navigation'
3782                             },
3783                             {
3784                                 tag: 'span',
3785                                 cls: 'icon-bar'
3786                             },
3787                             {
3788                                 tag: 'span',
3789                                 cls: 'icon-bar'
3790                             },
3791                             {
3792                                 tag: 'span',
3793                                 cls: 'icon-bar'
3794                             }
3795                         ]
3796                     }
3797                 ]
3798             });
3799         }
3800         
3801         cn.push({
3802             tag: 'div',
3803             cls: 'collapse navbar-collapse',
3804             cn : []
3805         });
3806         
3807         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3808         
3809         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3810             cfg.cls += ' navbar-' + this.position;
3811             
3812             // tag can override this..
3813             
3814             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3815         }
3816         
3817         if (this.brand !== '') {
3818             cn[0].cn.push({
3819                 tag: 'a',
3820                 href: this.brand_href ? this.brand_href : '#',
3821                 cls: 'navbar-brand',
3822                 cn: [
3823                 this.brand
3824                 ]
3825             });
3826         }
3827         
3828         if(this.main){
3829             cfg.cls += ' main-nav';
3830         }
3831         
3832         
3833         return cfg;
3834
3835         
3836     },
3837     getHeaderChildContainer : function()
3838     {
3839         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3840             return this.el.select('.navbar-header',true).first();
3841         }
3842         
3843         return this.getChildContainer();
3844     },
3845     
3846     
3847     initEvents : function()
3848     {
3849         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3850         
3851         if (this.autohide) {
3852             
3853             var prevScroll = 0;
3854             var ft = this.el;
3855             
3856             Roo.get(document).on('scroll',function(e) {
3857                 var ns = Roo.get(document).getScroll().top;
3858                 var os = prevScroll;
3859                 prevScroll = ns;
3860                 
3861                 if(ns > os){
3862                     ft.removeClass('slideDown');
3863                     ft.addClass('slideUp');
3864                     return;
3865                 }
3866                 ft.removeClass('slideUp');
3867                 ft.addClass('slideDown');
3868                  
3869               
3870           },this);
3871         }
3872     }    
3873     
3874 });
3875
3876
3877
3878  
3879
3880  /*
3881  * - LGPL
3882  *
3883  * navbar
3884  * 
3885  */
3886
3887 /**
3888  * @class Roo.bootstrap.NavSidebar
3889  * @extends Roo.bootstrap.Navbar
3890  * Bootstrap Sidebar class
3891  * 
3892  * @constructor
3893  * Create a new Sidebar
3894  * @param {Object} config The config object
3895  */
3896
3897
3898 Roo.bootstrap.NavSidebar = function(config){
3899     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3900 };
3901
3902 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3903     
3904     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3905     
3906     getAutoCreate : function(){
3907         
3908         
3909         return  {
3910             tag: 'div',
3911             cls: 'sidebar sidebar-nav'
3912         };
3913     
3914         
3915     }
3916     
3917     
3918     
3919 });
3920
3921
3922
3923  
3924
3925  /*
3926  * - LGPL
3927  *
3928  * nav group
3929  * 
3930  */
3931
3932 /**
3933  * @class Roo.bootstrap.NavGroup
3934  * @extends Roo.bootstrap.Component
3935  * Bootstrap NavGroup class
3936  * @cfg {String} align (left|right)
3937  * @cfg {Boolean} inverse
3938  * @cfg {String} type (nav|pills|tab) default nav
3939  * @cfg {String} navId - reference Id for navbar.
3940
3941  * 
3942  * @constructor
3943  * Create a new nav group
3944  * @param {Object} config The config object
3945  */
3946
3947 Roo.bootstrap.NavGroup = function(config){
3948     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3949     this.navItems = [];
3950    
3951     Roo.bootstrap.NavGroup.register(this);
3952      this.addEvents({
3953         /**
3954              * @event changed
3955              * Fires when the active item changes
3956              * @param {Roo.bootstrap.NavGroup} this
3957              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3958              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3959          */
3960         'changed': true
3961      });
3962     
3963 };
3964
3965 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3966     
3967     align: '',
3968     inverse: false,
3969     form: false,
3970     type: 'nav',
3971     navId : '',
3972     // private
3973     
3974     navItems : false, 
3975     
3976     getAutoCreate : function()
3977     {
3978         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3979         
3980         cfg = {
3981             tag : 'ul',
3982             cls: 'nav' 
3983         };
3984         
3985         if (['tabs','pills'].indexOf(this.type)!==-1) {
3986             cfg.cls += ' nav-' + this.type
3987         } else {
3988             if (this.type!=='nav') {
3989                 Roo.log('nav type must be nav/tabs/pills')
3990             }
3991             cfg.cls += ' navbar-nav'
3992         }
3993         
3994         if (this.parent().sidebar) {
3995             cfg = {
3996                 tag: 'ul',
3997                 cls: 'dashboard-menu sidebar-menu'
3998             };
3999             
4000             return cfg;
4001         }
4002         
4003         if (this.form === true) {
4004             cfg = {
4005                 tag: 'form',
4006                 cls: 'navbar-form'
4007             };
4008             
4009             if (this.align === 'right') {
4010                 cfg.cls += ' navbar-right';
4011             } else {
4012                 cfg.cls += ' navbar-left';
4013             }
4014         }
4015         
4016         if (this.align === 'right') {
4017             cfg.cls += ' navbar-right';
4018         }
4019         
4020         if (this.inverse) {
4021             cfg.cls += ' navbar-inverse';
4022             
4023         }
4024         
4025         
4026         return cfg;
4027     },
4028     /**
4029     * sets the active Navigation item
4030     * @param {Roo.bootstrap.NavItem} the new current navitem
4031     */
4032     setActiveItem : function(item)
4033     {
4034         var prev = false;
4035         Roo.each(this.navItems, function(v){
4036             if (v == item) {
4037                 return ;
4038             }
4039             if (v.isActive()) {
4040                 v.setActive(false, true);
4041                 prev = v;
4042                 
4043             }
4044             
4045         });
4046
4047         item.setActive(true, true);
4048         this.fireEvent('changed', this, item, prev);
4049         
4050         
4051     },
4052     /**
4053     * gets the active Navigation item
4054     * @return {Roo.bootstrap.NavItem} the current navitem
4055     */
4056     getActive : function()
4057     {
4058         
4059         var prev = false;
4060         Roo.each(this.navItems, function(v){
4061             
4062             if (v.isActive()) {
4063                 prev = v;
4064                 
4065             }
4066             
4067         });
4068         return prev;
4069     },
4070     
4071     indexOfNav : function()
4072     {
4073         
4074         var prev = false;
4075         Roo.each(this.navItems, function(v,i){
4076             
4077             if (v.isActive()) {
4078                 prev = i;
4079                 
4080             }
4081             
4082         });
4083         return prev;
4084     },
4085     /**
4086     * adds a Navigation item
4087     * @param {Roo.bootstrap.NavItem} the navitem to add
4088     */
4089     addItem : function(cfg)
4090     {
4091         var cn = new Roo.bootstrap.NavItem(cfg);
4092         this.register(cn);
4093         cn.parentId = this.id;
4094         cn.onRender(this.el, null);
4095         return cn;
4096     },
4097     /**
4098     * register a Navigation item
4099     * @param {Roo.bootstrap.NavItem} the navitem to add
4100     */
4101     register : function(item)
4102     {
4103         this.navItems.push( item);
4104         item.navId = this.navId;
4105     
4106     },
4107     
4108     /**
4109     * clear all the Navigation item
4110     */
4111    
4112     clearAll : function()
4113     {
4114         this.navItems = [];
4115         this.el.dom.innerHTML = '';
4116     },
4117     
4118     getNavItem: function(tabId)
4119     {
4120         var ret = false;
4121         Roo.each(this.navItems, function(e) {
4122             if (e.tabId == tabId) {
4123                ret =  e;
4124                return false;
4125             }
4126             return true;
4127             
4128         });
4129         return ret;
4130     },
4131     
4132     setActiveNext : function()
4133     {
4134         var i = this.indexOfNav(this.getActive());
4135         if (i > this.navItems.length) {
4136             return;
4137         }
4138         this.setActiveItem(this.navItems[i+1]);
4139     },
4140     setActivePrev : function()
4141     {
4142         var i = this.indexOfNav(this.getActive());
4143         if (i  < 1) {
4144             return;
4145         }
4146         this.setActiveItem(this.navItems[i-1]);
4147     },
4148     clearWasActive : function(except) {
4149         Roo.each(this.navItems, function(e) {
4150             if (e.tabId != except.tabId && e.was_active) {
4151                e.was_active = false;
4152                return false;
4153             }
4154             return true;
4155             
4156         });
4157     },
4158     getWasActive : function ()
4159     {
4160         var r = false;
4161         Roo.each(this.navItems, function(e) {
4162             if (e.was_active) {
4163                r = e;
4164                return false;
4165             }
4166             return true;
4167             
4168         });
4169         return r;
4170     }
4171     
4172     
4173 });
4174
4175  
4176 Roo.apply(Roo.bootstrap.NavGroup, {
4177     
4178     groups: {},
4179      /**
4180     * register a Navigation Group
4181     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4182     */
4183     register : function(navgrp)
4184     {
4185         this.groups[navgrp.navId] = navgrp;
4186         
4187     },
4188     /**
4189     * fetch a Navigation Group based on the navigation ID
4190     * @param {string} the navgroup to add
4191     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4192     */
4193     get: function(navId) {
4194         if (typeof(this.groups[navId]) == 'undefined') {
4195             return false;
4196             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4197         }
4198         return this.groups[navId] ;
4199     }
4200     
4201     
4202     
4203 });
4204
4205  /*
4206  * - LGPL
4207  *
4208  * row
4209  * 
4210  */
4211
4212 /**
4213  * @class Roo.bootstrap.NavItem
4214  * @extends Roo.bootstrap.Component
4215  * Bootstrap Navbar.NavItem class
4216  * @cfg {String} href  link to
4217  * @cfg {String} html content of button
4218  * @cfg {String} badge text inside badge
4219  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4220  * @cfg {String} glyphicon name of glyphicon
4221  * @cfg {String} icon name of font awesome icon
4222  * @cfg {Boolean} active Is item active
4223  * @cfg {Boolean} disabled Is item disabled
4224  
4225  * @cfg {Boolean} preventDefault (true | false) default false
4226  * @cfg {String} tabId the tab that this item activates.
4227  * @cfg {String} tagtype (a|span) render as a href or span?
4228  * @cfg {Boolean} animateRef (true|false) link to element default false  
4229   
4230  * @constructor
4231  * Create a new Navbar Item
4232  * @param {Object} config The config object
4233  */
4234 Roo.bootstrap.NavItem = function(config){
4235     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4236     this.addEvents({
4237         // raw events
4238         /**
4239          * @event click
4240          * The raw click event for the entire grid.
4241          * @param {Roo.EventObject} e
4242          */
4243         "click" : true,
4244          /**
4245             * @event changed
4246             * Fires when the active item active state changes
4247             * @param {Roo.bootstrap.NavItem} this
4248             * @param {boolean} state the new state
4249              
4250          */
4251         'changed': true,
4252         /**
4253             * @event scrollto
4254             * Fires when scroll to element
4255             * @param {Roo.bootstrap.NavItem} this
4256             * @param {Object} options
4257             * @param {Roo.EventObject} e
4258              
4259          */
4260         'scrollto': true
4261     });
4262    
4263 };
4264
4265 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4266     
4267     href: false,
4268     html: '',
4269     badge: '',
4270     icon: false,
4271     glyphicon: false,
4272     active: false,
4273     preventDefault : false,
4274     tabId : false,
4275     tagtype : 'a',
4276     disabled : false,
4277     animateRef : false,
4278     was_active : false,
4279     
4280     getAutoCreate : function(){
4281          
4282         var cfg = {
4283             tag: 'li',
4284             cls: 'nav-item'
4285             
4286         };
4287         
4288         if (this.active) {
4289             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4290         }
4291         if (this.disabled) {
4292             cfg.cls += ' disabled';
4293         }
4294         
4295         if (this.href || this.html || this.glyphicon || this.icon) {
4296             cfg.cn = [
4297                 {
4298                     tag: this.tagtype,
4299                     href : this.href || "#",
4300                     html: this.html || ''
4301                 }
4302             ];
4303             
4304             if (this.icon) {
4305                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4306             }
4307
4308             if(this.glyphicon) {
4309                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4310             }
4311             
4312             if (this.menu) {
4313                 
4314                 cfg.cn[0].html += " <span class='caret'></span>";
4315              
4316             }
4317             
4318             if (this.badge !== '') {
4319                  
4320                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4321             }
4322         }
4323         
4324         
4325         
4326         return cfg;
4327     },
4328     initEvents: function() 
4329     {
4330         if (typeof (this.menu) != 'undefined') {
4331             this.menu.parentType = this.xtype;
4332             this.menu.triggerEl = this.el;
4333             this.menu = this.addxtype(Roo.apply({}, this.menu));
4334         }
4335         
4336         this.el.select('a',true).on('click', this.onClick, this);
4337         
4338         if(this.tagtype == 'span'){
4339             this.el.select('span',true).on('click', this.onClick, this);
4340         }
4341        
4342         // at this point parent should be available..
4343         this.parent().register(this);
4344     },
4345     
4346     onClick : function(e)
4347     {
4348         if (e.getTarget('.dropdown-menu-item')) {
4349             // did you click on a menu itemm.... - then don't trigger onclick..
4350             return;
4351         }
4352         
4353         if(
4354                 this.preventDefault || 
4355                 this.href == '#' 
4356         ){
4357             Roo.log("NavItem - prevent Default?");
4358             e.preventDefault();
4359         }
4360         
4361         if (this.disabled) {
4362             return;
4363         }
4364         
4365         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4366         if (tg && tg.transition) {
4367             Roo.log("waiting for the transitionend");
4368             return;
4369         }
4370         
4371         
4372         
4373         //Roo.log("fire event clicked");
4374         if(this.fireEvent('click', this, e) === false){
4375             return;
4376         };
4377         
4378         if(this.tagtype == 'span'){
4379             return;
4380         }
4381         
4382         //Roo.log(this.href);
4383         var ael = this.el.select('a',true).first();
4384         //Roo.log(ael);
4385         
4386         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4387             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4388             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4389                 return; // ignore... - it's a 'hash' to another page.
4390             }
4391             Roo.log("NavItem - prevent Default?");
4392             e.preventDefault();
4393             this.scrollToElement(e);
4394         }
4395         
4396         
4397         var p =  this.parent();
4398    
4399         if (['tabs','pills'].indexOf(p.type)!==-1) {
4400             if (typeof(p.setActiveItem) !== 'undefined') {
4401                 p.setActiveItem(this);
4402             }
4403         }
4404         
4405         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4406         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4407             // remove the collapsed menu expand...
4408             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4409         }
4410     },
4411     
4412     isActive: function () {
4413         return this.active
4414     },
4415     setActive : function(state, fire, is_was_active)
4416     {
4417         if (this.active && !state && this.navId) {
4418             this.was_active = true;
4419             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4420             if (nv) {
4421                 nv.clearWasActive(this);
4422             }
4423             
4424         }
4425         this.active = state;
4426         
4427         if (!state ) {
4428             this.el.removeClass('active');
4429         } else if (!this.el.hasClass('active')) {
4430             this.el.addClass('active');
4431         }
4432         if (fire) {
4433             this.fireEvent('changed', this, state);
4434         }
4435         
4436         // show a panel if it's registered and related..
4437         
4438         if (!this.navId || !this.tabId || !state || is_was_active) {
4439             return;
4440         }
4441         
4442         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4443         if (!tg) {
4444             return;
4445         }
4446         var pan = tg.getPanelByName(this.tabId);
4447         if (!pan) {
4448             return;
4449         }
4450         // if we can not flip to new panel - go back to old nav highlight..
4451         if (false == tg.showPanel(pan)) {
4452             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4453             if (nv) {
4454                 var onav = nv.getWasActive();
4455                 if (onav) {
4456                     onav.setActive(true, false, true);
4457                 }
4458             }
4459             
4460         }
4461         
4462         
4463         
4464     },
4465      // this should not be here...
4466     setDisabled : function(state)
4467     {
4468         this.disabled = state;
4469         if (!state ) {
4470             this.el.removeClass('disabled');
4471         } else if (!this.el.hasClass('disabled')) {
4472             this.el.addClass('disabled');
4473         }
4474         
4475     },
4476     
4477     /**
4478      * Fetch the element to display the tooltip on.
4479      * @return {Roo.Element} defaults to this.el
4480      */
4481     tooltipEl : function()
4482     {
4483         return this.el.select('' + this.tagtype + '', true).first();
4484     },
4485     
4486     scrollToElement : function(e)
4487     {
4488         var c = document.body;
4489         
4490         /*
4491          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4492          */
4493         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4494             c = document.documentElement;
4495         }
4496         
4497         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4498         
4499         if(!target){
4500             return;
4501         }
4502
4503         var o = target.calcOffsetsTo(c);
4504         
4505         var options = {
4506             target : target,
4507             value : o[1]
4508         };
4509         
4510         this.fireEvent('scrollto', this, options, e);
4511         
4512         Roo.get(c).scrollTo('top', options.value, true);
4513         
4514         return;
4515     }
4516 });
4517  
4518
4519  /*
4520  * - LGPL
4521  *
4522  * sidebar item
4523  *
4524  *  li
4525  *    <span> icon </span>
4526  *    <span> text </span>
4527  *    <span>badge </span>
4528  */
4529
4530 /**
4531  * @class Roo.bootstrap.NavSidebarItem
4532  * @extends Roo.bootstrap.NavItem
4533  * Bootstrap Navbar.NavSidebarItem class
4534  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4535  * {bool} open is the menu open
4536  * @constructor
4537  * Create a new Navbar Button
4538  * @param {Object} config The config object
4539  */
4540 Roo.bootstrap.NavSidebarItem = function(config){
4541     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4542     this.addEvents({
4543         // raw events
4544         /**
4545          * @event click
4546          * The raw click event for the entire grid.
4547          * @param {Roo.EventObject} e
4548          */
4549         "click" : true,
4550          /**
4551             * @event changed
4552             * Fires when the active item active state changes
4553             * @param {Roo.bootstrap.NavSidebarItem} this
4554             * @param {boolean} state the new state
4555              
4556          */
4557         'changed': true
4558     });
4559    
4560 };
4561
4562 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4563     
4564     badgeWeight : 'default',
4565     
4566     open: false,
4567     
4568     getAutoCreate : function(){
4569         
4570         
4571         var a = {
4572                 tag: 'a',
4573                 href : this.href || '#',
4574                 cls: '',
4575                 html : '',
4576                 cn : []
4577         };
4578         var cfg = {
4579             tag: 'li',
4580             cls: '',
4581             cn: [ a ]
4582         };
4583         var span = {
4584             tag: 'span',
4585             html : this.html || ''
4586         };
4587         
4588         
4589         if (this.active) {
4590             cfg.cls += ' active';
4591         }
4592         
4593         if (this.disabled) {
4594             cfg.cls += ' disabled';
4595         }
4596         if (this.open) {
4597             cfg.cls += ' open x-open';
4598         }
4599         // left icon..
4600         if (this.glyphicon || this.icon) {
4601             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4602             a.cn.push({ tag : 'i', cls : c }) ;
4603         }
4604         // html..
4605         a.cn.push(span);
4606         // then badge..
4607         if (this.badge !== '') {
4608             
4609             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4610         }
4611         // fi
4612         if (this.menu) {
4613             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4614             a.cls += 'dropdown-toggle treeview' ;
4615         }
4616         
4617         return cfg;
4618          
4619            
4620     },
4621     
4622     initEvents : function()
4623     { 
4624         if (typeof (this.menu) != 'undefined') {
4625             this.menu.parentType = this.xtype;
4626             this.menu.triggerEl = this.el;
4627             this.menu = this.addxtype(Roo.apply({}, this.menu));
4628         }
4629         
4630         this.el.on('click', this.onClick, this);
4631        
4632     
4633         if(this.badge !== ''){
4634  
4635             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4636         }
4637         
4638     },
4639     
4640     onClick : function(e)
4641     {
4642         if(this.disabled){
4643             e.preventDefault();
4644             return;
4645         }
4646         
4647         if(this.preventDefault){
4648             e.preventDefault();
4649         }
4650         
4651         this.fireEvent('click', this);
4652     },
4653     
4654     disable : function()
4655     {
4656         this.setDisabled(true);
4657     },
4658     
4659     enable : function()
4660     {
4661         this.setDisabled(false);
4662     },
4663     
4664     setDisabled : function(state)
4665     {
4666         if(this.disabled == state){
4667             return;
4668         }
4669         
4670         this.disabled = state;
4671         
4672         if (state) {
4673             this.el.addClass('disabled');
4674             return;
4675         }
4676         
4677         this.el.removeClass('disabled');
4678         
4679         return;
4680     },
4681     
4682     setActive : function(state)
4683     {
4684         if(this.active == state){
4685             return;
4686         }
4687         
4688         this.active = state;
4689         
4690         if (state) {
4691             this.el.addClass('active');
4692             return;
4693         }
4694         
4695         this.el.removeClass('active');
4696         
4697         return;
4698     },
4699     
4700     isActive: function () 
4701     {
4702         return this.active;
4703     },
4704     
4705     setBadge : function(str)
4706     {
4707         if(!this.badgeEl){
4708             return;
4709         }
4710         
4711         this.badgeEl.dom.innerHTML = str;
4712     }
4713     
4714    
4715      
4716  
4717 });
4718  
4719
4720  /*
4721  * - LGPL
4722  *
4723  * row
4724  * 
4725  */
4726
4727 /**
4728  * @class Roo.bootstrap.Row
4729  * @extends Roo.bootstrap.Component
4730  * Bootstrap Row class (contains columns...)
4731  * 
4732  * @constructor
4733  * Create a new Row
4734  * @param {Object} config The config object
4735  */
4736
4737 Roo.bootstrap.Row = function(config){
4738     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4739 };
4740
4741 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4742     
4743     getAutoCreate : function(){
4744        return {
4745             cls: 'row clearfix'
4746        };
4747     }
4748     
4749     
4750 });
4751
4752  
4753
4754  /*
4755  * - LGPL
4756  *
4757  * element
4758  * 
4759  */
4760
4761 /**
4762  * @class Roo.bootstrap.Element
4763  * @extends Roo.bootstrap.Component
4764  * Bootstrap Element class
4765  * @cfg {String} html contents of the element
4766  * @cfg {String} tag tag of the element
4767  * @cfg {String} cls class of the element
4768  * @cfg {Boolean} preventDefault (true|false) default false
4769  * @cfg {Boolean} clickable (true|false) default false
4770  * 
4771  * @constructor
4772  * Create a new Element
4773  * @param {Object} config The config object
4774  */
4775
4776 Roo.bootstrap.Element = function(config){
4777     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4778     
4779     this.addEvents({
4780         // raw events
4781         /**
4782          * @event click
4783          * When a element is chick
4784          * @param {Roo.bootstrap.Element} this
4785          * @param {Roo.EventObject} e
4786          */
4787         "click" : true
4788     });
4789 };
4790
4791 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4792     
4793     tag: 'div',
4794     cls: '',
4795     html: '',
4796     preventDefault: false, 
4797     clickable: false,
4798     
4799     getAutoCreate : function(){
4800         
4801         var cfg = {
4802             tag: this.tag,
4803             cls: this.cls,
4804             html: this.html
4805         };
4806         
4807         return cfg;
4808     },
4809     
4810     initEvents: function() 
4811     {
4812         Roo.bootstrap.Element.superclass.initEvents.call(this);
4813         
4814         if(this.clickable){
4815             this.el.on('click', this.onClick, this);
4816         }
4817         
4818     },
4819     
4820     onClick : function(e)
4821     {
4822         if(this.preventDefault){
4823             e.preventDefault();
4824         }
4825         
4826         this.fireEvent('click', this, e);
4827     },
4828     
4829     getValue : function()
4830     {
4831         return this.el.dom.innerHTML;
4832     },
4833     
4834     setValue : function(value)
4835     {
4836         this.el.dom.innerHTML = value;
4837     }
4838    
4839 });
4840
4841  
4842
4843  /*
4844  * - LGPL
4845  *
4846  * pagination
4847  * 
4848  */
4849
4850 /**
4851  * @class Roo.bootstrap.Pagination
4852  * @extends Roo.bootstrap.Component
4853  * Bootstrap Pagination class
4854  * @cfg {String} size xs | sm | md | lg
4855  * @cfg {Boolean} inverse false | true
4856  * 
4857  * @constructor
4858  * Create a new Pagination
4859  * @param {Object} config The config object
4860  */
4861
4862 Roo.bootstrap.Pagination = function(config){
4863     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4864 };
4865
4866 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4867     
4868     cls: false,
4869     size: false,
4870     inverse: false,
4871     
4872     getAutoCreate : function(){
4873         var cfg = {
4874             tag: 'ul',
4875                 cls: 'pagination'
4876         };
4877         if (this.inverse) {
4878             cfg.cls += ' inverse';
4879         }
4880         if (this.html) {
4881             cfg.html=this.html;
4882         }
4883         if (this.cls) {
4884             cfg.cls += " " + this.cls;
4885         }
4886         return cfg;
4887     }
4888    
4889 });
4890
4891  
4892
4893  /*
4894  * - LGPL
4895  *
4896  * Pagination item
4897  * 
4898  */
4899
4900
4901 /**
4902  * @class Roo.bootstrap.PaginationItem
4903  * @extends Roo.bootstrap.Component
4904  * Bootstrap PaginationItem class
4905  * @cfg {String} html text
4906  * @cfg {String} href the link
4907  * @cfg {Boolean} preventDefault (true | false) default true
4908  * @cfg {Boolean} active (true | false) default false
4909  * @cfg {Boolean} disabled default false
4910  * 
4911  * 
4912  * @constructor
4913  * Create a new PaginationItem
4914  * @param {Object} config The config object
4915  */
4916
4917
4918 Roo.bootstrap.PaginationItem = function(config){
4919     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4920     this.addEvents({
4921         // raw events
4922         /**
4923          * @event click
4924          * The raw click event for the entire grid.
4925          * @param {Roo.EventObject} e
4926          */
4927         "click" : true
4928     });
4929 };
4930
4931 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4932     
4933     href : false,
4934     html : false,
4935     preventDefault: true,
4936     active : false,
4937     cls : false,
4938     disabled: false,
4939     
4940     getAutoCreate : function(){
4941         var cfg= {
4942             tag: 'li',
4943             cn: [
4944                 {
4945                     tag : 'a',
4946                     href : this.href ? this.href : '#',
4947                     html : this.html ? this.html : ''
4948                 }
4949             ]
4950         };
4951         
4952         if(this.cls){
4953             cfg.cls = this.cls;
4954         }
4955         
4956         if(this.disabled){
4957             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4958         }
4959         
4960         if(this.active){
4961             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4962         }
4963         
4964         return cfg;
4965     },
4966     
4967     initEvents: function() {
4968         
4969         this.el.on('click', this.onClick, this);
4970         
4971     },
4972     onClick : function(e)
4973     {
4974         Roo.log('PaginationItem on click ');
4975         if(this.preventDefault){
4976             e.preventDefault();
4977         }
4978         
4979         if(this.disabled){
4980             return;
4981         }
4982         
4983         this.fireEvent('click', this, e);
4984     }
4985    
4986 });
4987
4988  
4989
4990  /*
4991  * - LGPL
4992  *
4993  * slider
4994  * 
4995  */
4996
4997
4998 /**
4999  * @class Roo.bootstrap.Slider
5000  * @extends Roo.bootstrap.Component
5001  * Bootstrap Slider class
5002  *    
5003  * @constructor
5004  * Create a new Slider
5005  * @param {Object} config The config object
5006  */
5007
5008 Roo.bootstrap.Slider = function(config){
5009     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5010 };
5011
5012 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5013     
5014     getAutoCreate : function(){
5015         
5016         var cfg = {
5017             tag: 'div',
5018             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5019             cn: [
5020                 {
5021                     tag: 'a',
5022                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5023                 }
5024             ]
5025         };
5026         
5027         return cfg;
5028     }
5029    
5030 });
5031
5032  /*
5033  * Based on:
5034  * Ext JS Library 1.1.1
5035  * Copyright(c) 2006-2007, Ext JS, LLC.
5036  *
5037  * Originally Released Under LGPL - original licence link has changed is not relivant.
5038  *
5039  * Fork - LGPL
5040  * <script type="text/javascript">
5041  */
5042  
5043
5044 /**
5045  * @class Roo.grid.ColumnModel
5046  * @extends Roo.util.Observable
5047  * This is the default implementation of a ColumnModel used by the Grid. It defines
5048  * the columns in the grid.
5049  * <br>Usage:<br>
5050  <pre><code>
5051  var colModel = new Roo.grid.ColumnModel([
5052         {header: "Ticker", width: 60, sortable: true, locked: true},
5053         {header: "Company Name", width: 150, sortable: true},
5054         {header: "Market Cap.", width: 100, sortable: true},
5055         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5056         {header: "Employees", width: 100, sortable: true, resizable: false}
5057  ]);
5058  </code></pre>
5059  * <p>
5060  
5061  * The config options listed for this class are options which may appear in each
5062  * individual column definition.
5063  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5064  * @constructor
5065  * @param {Object} config An Array of column config objects. See this class's
5066  * config objects for details.
5067 */
5068 Roo.grid.ColumnModel = function(config){
5069         /**
5070      * The config passed into the constructor
5071      */
5072     this.config = config;
5073     this.lookup = {};
5074
5075     // if no id, create one
5076     // if the column does not have a dataIndex mapping,
5077     // map it to the order it is in the config
5078     for(var i = 0, len = config.length; i < len; i++){
5079         var c = config[i];
5080         if(typeof c.dataIndex == "undefined"){
5081             c.dataIndex = i;
5082         }
5083         if(typeof c.renderer == "string"){
5084             c.renderer = Roo.util.Format[c.renderer];
5085         }
5086         if(typeof c.id == "undefined"){
5087             c.id = Roo.id();
5088         }
5089         if(c.editor && c.editor.xtype){
5090             c.editor  = Roo.factory(c.editor, Roo.grid);
5091         }
5092         if(c.editor && c.editor.isFormField){
5093             c.editor = new Roo.grid.GridEditor(c.editor);
5094         }
5095         this.lookup[c.id] = c;
5096     }
5097
5098     /**
5099      * The width of columns which have no width specified (defaults to 100)
5100      * @type Number
5101      */
5102     this.defaultWidth = 100;
5103
5104     /**
5105      * Default sortable of columns which have no sortable specified (defaults to false)
5106      * @type Boolean
5107      */
5108     this.defaultSortable = false;
5109
5110     this.addEvents({
5111         /**
5112              * @event widthchange
5113              * Fires when the width of a column changes.
5114              * @param {ColumnModel} this
5115              * @param {Number} columnIndex The column index
5116              * @param {Number} newWidth The new width
5117              */
5118             "widthchange": true,
5119         /**
5120              * @event headerchange
5121              * Fires when the text of a header changes.
5122              * @param {ColumnModel} this
5123              * @param {Number} columnIndex The column index
5124              * @param {Number} newText The new header text
5125              */
5126             "headerchange": true,
5127         /**
5128              * @event hiddenchange
5129              * Fires when a column is hidden or "unhidden".
5130              * @param {ColumnModel} this
5131              * @param {Number} columnIndex The column index
5132              * @param {Boolean} hidden true if hidden, false otherwise
5133              */
5134             "hiddenchange": true,
5135             /**
5136          * @event columnmoved
5137          * Fires when a column is moved.
5138          * @param {ColumnModel} this
5139          * @param {Number} oldIndex
5140          * @param {Number} newIndex
5141          */
5142         "columnmoved" : true,
5143         /**
5144          * @event columlockchange
5145          * Fires when a column's locked state is changed
5146          * @param {ColumnModel} this
5147          * @param {Number} colIndex
5148          * @param {Boolean} locked true if locked
5149          */
5150         "columnlockchange" : true
5151     });
5152     Roo.grid.ColumnModel.superclass.constructor.call(this);
5153 };
5154 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5155     /**
5156      * @cfg {String} header The header text to display in the Grid view.
5157      */
5158     /**
5159      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5160      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5161      * specified, the column's index is used as an index into the Record's data Array.
5162      */
5163     /**
5164      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5165      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5166      */
5167     /**
5168      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5169      * Defaults to the value of the {@link #defaultSortable} property.
5170      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5171      */
5172     /**
5173      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5174      */
5175     /**
5176      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5177      */
5178     /**
5179      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5180      */
5181     /**
5182      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5183      */
5184     /**
5185      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5186      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5187      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5188      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5189      */
5190        /**
5191      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5192      */
5193     /**
5194      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5195      */
5196     /**
5197      * @cfg {String} cursor (Optional)
5198      */
5199     /**
5200      * @cfg {String} tooltip (Optional)
5201      */
5202     /**
5203      * @cfg {Number} xs (Optional)
5204      */
5205     /**
5206      * @cfg {Number} sm (Optional)
5207      */
5208     /**
5209      * @cfg {Number} md (Optional)
5210      */
5211     /**
5212      * @cfg {Number} lg (Optional)
5213      */
5214     /**
5215      * Returns the id of the column at the specified index.
5216      * @param {Number} index The column index
5217      * @return {String} the id
5218      */
5219     getColumnId : function(index){
5220         return this.config[index].id;
5221     },
5222
5223     /**
5224      * Returns the column for a specified id.
5225      * @param {String} id The column id
5226      * @return {Object} the column
5227      */
5228     getColumnById : function(id){
5229         return this.lookup[id];
5230     },
5231
5232     
5233     /**
5234      * Returns the column for a specified dataIndex.
5235      * @param {String} dataIndex The column dataIndex
5236      * @return {Object|Boolean} the column or false if not found
5237      */
5238     getColumnByDataIndex: function(dataIndex){
5239         var index = this.findColumnIndex(dataIndex);
5240         return index > -1 ? this.config[index] : false;
5241     },
5242     
5243     /**
5244      * Returns the index for a specified column id.
5245      * @param {String} id The column id
5246      * @return {Number} the index, or -1 if not found
5247      */
5248     getIndexById : function(id){
5249         for(var i = 0, len = this.config.length; i < len; i++){
5250             if(this.config[i].id == id){
5251                 return i;
5252             }
5253         }
5254         return -1;
5255     },
5256     
5257     /**
5258      * Returns the index for a specified column dataIndex.
5259      * @param {String} dataIndex The column dataIndex
5260      * @return {Number} the index, or -1 if not found
5261      */
5262     
5263     findColumnIndex : function(dataIndex){
5264         for(var i = 0, len = this.config.length; i < len; i++){
5265             if(this.config[i].dataIndex == dataIndex){
5266                 return i;
5267             }
5268         }
5269         return -1;
5270     },
5271     
5272     
5273     moveColumn : function(oldIndex, newIndex){
5274         var c = this.config[oldIndex];
5275         this.config.splice(oldIndex, 1);
5276         this.config.splice(newIndex, 0, c);
5277         this.dataMap = null;
5278         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5279     },
5280
5281     isLocked : function(colIndex){
5282         return this.config[colIndex].locked === true;
5283     },
5284
5285     setLocked : function(colIndex, value, suppressEvent){
5286         if(this.isLocked(colIndex) == value){
5287             return;
5288         }
5289         this.config[colIndex].locked = value;
5290         if(!suppressEvent){
5291             this.fireEvent("columnlockchange", this, colIndex, value);
5292         }
5293     },
5294
5295     getTotalLockedWidth : function(){
5296         var totalWidth = 0;
5297         for(var i = 0; i < this.config.length; i++){
5298             if(this.isLocked(i) && !this.isHidden(i)){
5299                 this.totalWidth += this.getColumnWidth(i);
5300             }
5301         }
5302         return totalWidth;
5303     },
5304
5305     getLockedCount : function(){
5306         for(var i = 0, len = this.config.length; i < len; i++){
5307             if(!this.isLocked(i)){
5308                 return i;
5309             }
5310         }
5311         
5312         return this.config.length;
5313     },
5314
5315     /**
5316      * Returns the number of columns.
5317      * @return {Number}
5318      */
5319     getColumnCount : function(visibleOnly){
5320         if(visibleOnly === true){
5321             var c = 0;
5322             for(var i = 0, len = this.config.length; i < len; i++){
5323                 if(!this.isHidden(i)){
5324                     c++;
5325                 }
5326             }
5327             return c;
5328         }
5329         return this.config.length;
5330     },
5331
5332     /**
5333      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5334      * @param {Function} fn
5335      * @param {Object} scope (optional)
5336      * @return {Array} result
5337      */
5338     getColumnsBy : function(fn, scope){
5339         var r = [];
5340         for(var i = 0, len = this.config.length; i < len; i++){
5341             var c = this.config[i];
5342             if(fn.call(scope||this, c, i) === true){
5343                 r[r.length] = c;
5344             }
5345         }
5346         return r;
5347     },
5348
5349     /**
5350      * Returns true if the specified column is sortable.
5351      * @param {Number} col The column index
5352      * @return {Boolean}
5353      */
5354     isSortable : function(col){
5355         if(typeof this.config[col].sortable == "undefined"){
5356             return this.defaultSortable;
5357         }
5358         return this.config[col].sortable;
5359     },
5360
5361     /**
5362      * Returns the rendering (formatting) function defined for the column.
5363      * @param {Number} col The column index.
5364      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5365      */
5366     getRenderer : function(col){
5367         if(!this.config[col].renderer){
5368             return Roo.grid.ColumnModel.defaultRenderer;
5369         }
5370         return this.config[col].renderer;
5371     },
5372
5373     /**
5374      * Sets the rendering (formatting) function for a column.
5375      * @param {Number} col The column index
5376      * @param {Function} fn The function to use to process the cell's raw data
5377      * to return HTML markup for the grid view. The render function is called with
5378      * the following parameters:<ul>
5379      * <li>Data value.</li>
5380      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5381      * <li>css A CSS style string to apply to the table cell.</li>
5382      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5383      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5384      * <li>Row index</li>
5385      * <li>Column index</li>
5386      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5387      */
5388     setRenderer : function(col, fn){
5389         this.config[col].renderer = fn;
5390     },
5391
5392     /**
5393      * Returns the width for the specified column.
5394      * @param {Number} col The column index
5395      * @return {Number}
5396      */
5397     getColumnWidth : function(col){
5398         return this.config[col].width * 1 || this.defaultWidth;
5399     },
5400
5401     /**
5402      * Sets the width for a column.
5403      * @param {Number} col The column index
5404      * @param {Number} width The new width
5405      */
5406     setColumnWidth : function(col, width, suppressEvent){
5407         this.config[col].width = width;
5408         this.totalWidth = null;
5409         if(!suppressEvent){
5410              this.fireEvent("widthchange", this, col, width);
5411         }
5412     },
5413
5414     /**
5415      * Returns the total width of all columns.
5416      * @param {Boolean} includeHidden True to include hidden column widths
5417      * @return {Number}
5418      */
5419     getTotalWidth : function(includeHidden){
5420         if(!this.totalWidth){
5421             this.totalWidth = 0;
5422             for(var i = 0, len = this.config.length; i < len; i++){
5423                 if(includeHidden || !this.isHidden(i)){
5424                     this.totalWidth += this.getColumnWidth(i);
5425                 }
5426             }
5427         }
5428         return this.totalWidth;
5429     },
5430
5431     /**
5432      * Returns the header for the specified column.
5433      * @param {Number} col The column index
5434      * @return {String}
5435      */
5436     getColumnHeader : function(col){
5437         return this.config[col].header;
5438     },
5439
5440     /**
5441      * Sets the header for a column.
5442      * @param {Number} col The column index
5443      * @param {String} header The new header
5444      */
5445     setColumnHeader : function(col, header){
5446         this.config[col].header = header;
5447         this.fireEvent("headerchange", this, col, header);
5448     },
5449
5450     /**
5451      * Returns the tooltip for the specified column.
5452      * @param {Number} col The column index
5453      * @return {String}
5454      */
5455     getColumnTooltip : function(col){
5456             return this.config[col].tooltip;
5457     },
5458     /**
5459      * Sets the tooltip for a column.
5460      * @param {Number} col The column index
5461      * @param {String} tooltip The new tooltip
5462      */
5463     setColumnTooltip : function(col, tooltip){
5464             this.config[col].tooltip = tooltip;
5465     },
5466
5467     /**
5468      * Returns the dataIndex for the specified column.
5469      * @param {Number} col The column index
5470      * @return {Number}
5471      */
5472     getDataIndex : function(col){
5473         return this.config[col].dataIndex;
5474     },
5475
5476     /**
5477      * Sets the dataIndex for a column.
5478      * @param {Number} col The column index
5479      * @param {Number} dataIndex The new dataIndex
5480      */
5481     setDataIndex : function(col, dataIndex){
5482         this.config[col].dataIndex = dataIndex;
5483     },
5484
5485     
5486     
5487     /**
5488      * Returns true if the cell is editable.
5489      * @param {Number} colIndex The column index
5490      * @param {Number} rowIndex The row index - this is nto actually used..?
5491      * @return {Boolean}
5492      */
5493     isCellEditable : function(colIndex, rowIndex){
5494         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5495     },
5496
5497     /**
5498      * Returns the editor defined for the cell/column.
5499      * return false or null to disable editing.
5500      * @param {Number} colIndex The column index
5501      * @param {Number} rowIndex The row index
5502      * @return {Object}
5503      */
5504     getCellEditor : function(colIndex, rowIndex){
5505         return this.config[colIndex].editor;
5506     },
5507
5508     /**
5509      * Sets if a column is editable.
5510      * @param {Number} col The column index
5511      * @param {Boolean} editable True if the column is editable
5512      */
5513     setEditable : function(col, editable){
5514         this.config[col].editable = editable;
5515     },
5516
5517
5518     /**
5519      * Returns true if the column is hidden.
5520      * @param {Number} colIndex The column index
5521      * @return {Boolean}
5522      */
5523     isHidden : function(colIndex){
5524         return this.config[colIndex].hidden;
5525     },
5526
5527
5528     /**
5529      * Returns true if the column width cannot be changed
5530      */
5531     isFixed : function(colIndex){
5532         return this.config[colIndex].fixed;
5533     },
5534
5535     /**
5536      * Returns true if the column can be resized
5537      * @return {Boolean}
5538      */
5539     isResizable : function(colIndex){
5540         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5541     },
5542     /**
5543      * Sets if a column is hidden.
5544      * @param {Number} colIndex The column index
5545      * @param {Boolean} hidden True if the column is hidden
5546      */
5547     setHidden : function(colIndex, hidden){
5548         this.config[colIndex].hidden = hidden;
5549         this.totalWidth = null;
5550         this.fireEvent("hiddenchange", this, colIndex, hidden);
5551     },
5552
5553     /**
5554      * Sets the editor for a column.
5555      * @param {Number} col The column index
5556      * @param {Object} editor The editor object
5557      */
5558     setEditor : function(col, editor){
5559         this.config[col].editor = editor;
5560     }
5561 });
5562
5563 Roo.grid.ColumnModel.defaultRenderer = function(value)
5564 {
5565     if(typeof value == "object") {
5566         return value;
5567     }
5568         if(typeof value == "string" && value.length < 1){
5569             return "&#160;";
5570         }
5571     
5572         return String.format("{0}", value);
5573 };
5574
5575 // Alias for backwards compatibility
5576 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5577 /*
5578  * Based on:
5579  * Ext JS Library 1.1.1
5580  * Copyright(c) 2006-2007, Ext JS, LLC.
5581  *
5582  * Originally Released Under LGPL - original licence link has changed is not relivant.
5583  *
5584  * Fork - LGPL
5585  * <script type="text/javascript">
5586  */
5587  
5588 /**
5589  * @class Roo.LoadMask
5590  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5591  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5592  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5593  * element's UpdateManager load indicator and will be destroyed after the initial load.
5594  * @constructor
5595  * Create a new LoadMask
5596  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5597  * @param {Object} config The config object
5598  */
5599 Roo.LoadMask = function(el, config){
5600     this.el = Roo.get(el);
5601     Roo.apply(this, config);
5602     if(this.store){
5603         this.store.on('beforeload', this.onBeforeLoad, this);
5604         this.store.on('load', this.onLoad, this);
5605         this.store.on('loadexception', this.onLoadException, this);
5606         this.removeMask = false;
5607     }else{
5608         var um = this.el.getUpdateManager();
5609         um.showLoadIndicator = false; // disable the default indicator
5610         um.on('beforeupdate', this.onBeforeLoad, this);
5611         um.on('update', this.onLoad, this);
5612         um.on('failure', this.onLoad, this);
5613         this.removeMask = true;
5614     }
5615 };
5616
5617 Roo.LoadMask.prototype = {
5618     /**
5619      * @cfg {Boolean} removeMask
5620      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5621      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5622      */
5623     /**
5624      * @cfg {String} msg
5625      * The text to display in a centered loading message box (defaults to 'Loading...')
5626      */
5627     msg : 'Loading...',
5628     /**
5629      * @cfg {String} msgCls
5630      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5631      */
5632     msgCls : 'x-mask-loading',
5633
5634     /**
5635      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5636      * @type Boolean
5637      */
5638     disabled: false,
5639
5640     /**
5641      * Disables the mask to prevent it from being displayed
5642      */
5643     disable : function(){
5644        this.disabled = true;
5645     },
5646
5647     /**
5648      * Enables the mask so that it can be displayed
5649      */
5650     enable : function(){
5651         this.disabled = false;
5652     },
5653     
5654     onLoadException : function()
5655     {
5656         Roo.log(arguments);
5657         
5658         if (typeof(arguments[3]) != 'undefined') {
5659             Roo.MessageBox.alert("Error loading",arguments[3]);
5660         } 
5661         /*
5662         try {
5663             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5664                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5665             }   
5666         } catch(e) {
5667             
5668         }
5669         */
5670     
5671         
5672         
5673         this.el.unmask(this.removeMask);
5674     },
5675     // private
5676     onLoad : function()
5677     {
5678         this.el.unmask(this.removeMask);
5679     },
5680
5681     // private
5682     onBeforeLoad : function(){
5683         if(!this.disabled){
5684             (function() { this.el.mask(this.msg, this.msgCls) }).defer(50, this);
5685         }
5686     },
5687
5688     // private
5689     destroy : function(){
5690         if(this.store){
5691             this.store.un('beforeload', this.onBeforeLoad, this);
5692             this.store.un('load', this.onLoad, this);
5693             this.store.un('loadexception', this.onLoadException, this);
5694         }else{
5695             var um = this.el.getUpdateManager();
5696             um.un('beforeupdate', this.onBeforeLoad, this);
5697             um.un('update', this.onLoad, this);
5698             um.un('failure', this.onLoad, this);
5699         }
5700     }
5701 };/*
5702  * - LGPL
5703  *
5704  * table
5705  * 
5706  */
5707
5708 /**
5709  * @class Roo.bootstrap.Table
5710  * @extends Roo.bootstrap.Component
5711  * Bootstrap Table class
5712  * @cfg {String} cls table class
5713  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5714  * @cfg {String} bgcolor Specifies the background color for a table
5715  * @cfg {Number} border Specifies whether the table cells should have borders or not
5716  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5717  * @cfg {Number} cellspacing Specifies the space between cells
5718  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5719  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5720  * @cfg {String} sortable Specifies that the table should be sortable
5721  * @cfg {String} summary Specifies a summary of the content of a table
5722  * @cfg {Number} width Specifies the width of a table
5723  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5724  * 
5725  * @cfg {boolean} striped Should the rows be alternative striped
5726  * @cfg {boolean} bordered Add borders to the table
5727  * @cfg {boolean} hover Add hover highlighting
5728  * @cfg {boolean} condensed Format condensed
5729  * @cfg {boolean} responsive Format condensed
5730  * @cfg {Boolean} loadMask (true|false) default false
5731  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5732  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5733  * @cfg {Boolean} rowSelection (true|false) default false
5734  * @cfg {Boolean} cellSelection (true|false) default false
5735  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5736  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5737  
5738  * 
5739  * @constructor
5740  * Create a new Table
5741  * @param {Object} config The config object
5742  */
5743
5744 Roo.bootstrap.Table = function(config){
5745     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5746     
5747   
5748     
5749     // BC...
5750     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5751     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5752     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5753     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5754     
5755     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5756     if (this.sm) {
5757         this.sm.grid = this;
5758         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5759         this.sm = this.selModel;
5760         this.sm.xmodule = this.xmodule || false;
5761     }
5762     
5763     if (this.cm && typeof(this.cm.config) == 'undefined') {
5764         this.colModel = new Roo.grid.ColumnModel(this.cm);
5765         this.cm = this.colModel;
5766         this.cm.xmodule = this.xmodule || false;
5767     }
5768     if (this.store) {
5769         this.store= Roo.factory(this.store, Roo.data);
5770         this.ds = this.store;
5771         this.ds.xmodule = this.xmodule || false;
5772          
5773     }
5774     if (this.footer && this.store) {
5775         this.footer.dataSource = this.ds;
5776         this.footer = Roo.factory(this.footer);
5777     }
5778     
5779     /** @private */
5780     this.addEvents({
5781         /**
5782          * @event cellclick
5783          * Fires when a cell is clicked
5784          * @param {Roo.bootstrap.Table} this
5785          * @param {Roo.Element} el
5786          * @param {Number} rowIndex
5787          * @param {Number} columnIndex
5788          * @param {Roo.EventObject} e
5789          */
5790         "cellclick" : true,
5791         /**
5792          * @event celldblclick
5793          * Fires when a cell is double clicked
5794          * @param {Roo.bootstrap.Table} this
5795          * @param {Roo.Element} el
5796          * @param {Number} rowIndex
5797          * @param {Number} columnIndex
5798          * @param {Roo.EventObject} e
5799          */
5800         "celldblclick" : true,
5801         /**
5802          * @event rowclick
5803          * Fires when a row is clicked
5804          * @param {Roo.bootstrap.Table} this
5805          * @param {Roo.Element} el
5806          * @param {Number} rowIndex
5807          * @param {Roo.EventObject} e
5808          */
5809         "rowclick" : true,
5810         /**
5811          * @event rowdblclick
5812          * Fires when a row is double clicked
5813          * @param {Roo.bootstrap.Table} this
5814          * @param {Roo.Element} el
5815          * @param {Number} rowIndex
5816          * @param {Roo.EventObject} e
5817          */
5818         "rowdblclick" : true,
5819         /**
5820          * @event mouseover
5821          * Fires when a mouseover occur
5822          * @param {Roo.bootstrap.Table} this
5823          * @param {Roo.Element} el
5824          * @param {Number} rowIndex
5825          * @param {Number} columnIndex
5826          * @param {Roo.EventObject} e
5827          */
5828         "mouseover" : true,
5829         /**
5830          * @event mouseout
5831          * Fires when a mouseout occur
5832          * @param {Roo.bootstrap.Table} this
5833          * @param {Roo.Element} el
5834          * @param {Number} rowIndex
5835          * @param {Number} columnIndex
5836          * @param {Roo.EventObject} e
5837          */
5838         "mouseout" : true,
5839         /**
5840          * @event rowclass
5841          * Fires when a row is rendered, so you can change add a style to it.
5842          * @param {Roo.bootstrap.Table} this
5843          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5844          */
5845         'rowclass' : true,
5846           /**
5847          * @event rowsrendered
5848          * Fires when all the  rows have been rendered
5849          * @param {Roo.bootstrap.Table} this
5850          */
5851         'rowsrendered' : true,
5852         /**
5853          * @event contextmenu
5854          * The raw contextmenu event for the entire grid.
5855          * @param {Roo.EventObject} e
5856          */
5857         "contextmenu" : true,
5858         /**
5859          * @event rowcontextmenu
5860          * Fires when a row is right clicked
5861          * @param {Roo.bootstrap.Table} this
5862          * @param {Number} rowIndex
5863          * @param {Roo.EventObject} e
5864          */
5865         "rowcontextmenu" : true,
5866         /**
5867          * @event cellcontextmenu
5868          * Fires when a cell is right clicked
5869          * @param {Roo.bootstrap.Table} this
5870          * @param {Number} rowIndex
5871          * @param {Number} cellIndex
5872          * @param {Roo.EventObject} e
5873          */
5874          "cellcontextmenu" : true,
5875          /**
5876          * @event headercontextmenu
5877          * Fires when a header is right clicked
5878          * @param {Roo.bootstrap.Table} this
5879          * @param {Number} columnIndex
5880          * @param {Roo.EventObject} e
5881          */
5882         "headercontextmenu" : true
5883     });
5884 };
5885
5886 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5887     
5888     cls: false,
5889     align: false,
5890     bgcolor: false,
5891     border: false,
5892     cellpadding: false,
5893     cellspacing: false,
5894     frame: false,
5895     rules: false,
5896     sortable: false,
5897     summary: false,
5898     width: false,
5899     striped : false,
5900     scrollBody : false,
5901     bordered: false,
5902     hover:  false,
5903     condensed : false,
5904     responsive : false,
5905     sm : false,
5906     cm : false,
5907     store : false,
5908     loadMask : false,
5909     footerShow : true,
5910     headerShow : true,
5911   
5912     rowSelection : false,
5913     cellSelection : false,
5914     layout : false,
5915     
5916     // Roo.Element - the tbody
5917     mainBody: false,
5918     // Roo.Element - thead element
5919     mainHead: false,
5920     
5921     container: false, // used by gridpanel...
5922     
5923     getAutoCreate : function()
5924     {
5925         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5926         
5927         cfg = {
5928             tag: 'table',
5929             cls : 'table',
5930             cn : []
5931         };
5932         if (this.scrollBody) {
5933             cfg.cls += ' table-body-fixed';
5934         }    
5935         if (this.striped) {
5936             cfg.cls += ' table-striped';
5937         }
5938         
5939         if (this.hover) {
5940             cfg.cls += ' table-hover';
5941         }
5942         if (this.bordered) {
5943             cfg.cls += ' table-bordered';
5944         }
5945         if (this.condensed) {
5946             cfg.cls += ' table-condensed';
5947         }
5948         if (this.responsive) {
5949             cfg.cls += ' table-responsive';
5950         }
5951         
5952         if (this.cls) {
5953             cfg.cls+=  ' ' +this.cls;
5954         }
5955         
5956         // this lot should be simplifed...
5957         
5958         if (this.align) {
5959             cfg.align=this.align;
5960         }
5961         if (this.bgcolor) {
5962             cfg.bgcolor=this.bgcolor;
5963         }
5964         if (this.border) {
5965             cfg.border=this.border;
5966         }
5967         if (this.cellpadding) {
5968             cfg.cellpadding=this.cellpadding;
5969         }
5970         if (this.cellspacing) {
5971             cfg.cellspacing=this.cellspacing;
5972         }
5973         if (this.frame) {
5974             cfg.frame=this.frame;
5975         }
5976         if (this.rules) {
5977             cfg.rules=this.rules;
5978         }
5979         if (this.sortable) {
5980             cfg.sortable=this.sortable;
5981         }
5982         if (this.summary) {
5983             cfg.summary=this.summary;
5984         }
5985         if (this.width) {
5986             cfg.width=this.width;
5987         }
5988         if (this.layout) {
5989             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5990         }
5991         
5992         if(this.store || this.cm){
5993             if(this.headerShow){
5994                 cfg.cn.push(this.renderHeader());
5995             }
5996             
5997             cfg.cn.push(this.renderBody());
5998             
5999             if(this.footerShow){
6000                 cfg.cn.push(this.renderFooter());
6001             }
6002             // where does this come from?
6003             //cfg.cls+=  ' TableGrid';
6004         }
6005         
6006         return { cn : [ cfg ] };
6007     },
6008     
6009     initEvents : function()
6010     {   
6011         if(!this.store || !this.cm){
6012             return;
6013         }
6014         if (this.selModel) {
6015             this.selModel.initEvents();
6016         }
6017         
6018         
6019         //Roo.log('initEvents with ds!!!!');
6020         
6021         this.mainBody = this.el.select('tbody', true).first();
6022         this.mainHead = this.el.select('thead', true).first();
6023         
6024         
6025         
6026         
6027         var _this = this;
6028         
6029         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6030             e.on('click', _this.sort, _this);
6031         });
6032         
6033         this.mainBody.on("click", this.onClick, this);
6034         this.mainBody.on("dblclick", this.onDblClick, this);
6035         
6036         // why is this done????? = it breaks dialogs??
6037         //this.parent().el.setStyle('position', 'relative');
6038         
6039         
6040         if (this.footer) {
6041             this.footer.parentId = this.id;
6042             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
6043         } 
6044         
6045         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6046         
6047         this.store.on('load', this.onLoad, this);
6048         this.store.on('beforeload', this.onBeforeLoad, this);
6049         this.store.on('update', this.onUpdate, this);
6050         this.store.on('add', this.onAdd, this);
6051         this.store.on("clear", this.clear, this);
6052         
6053         this.el.on("contextmenu", this.onContextMenu, this);
6054         
6055         this.mainBody.on('scroll', this.onBodyScroll, this);
6056         
6057         
6058     },
6059     
6060     onContextMenu : function(e, t)
6061     {
6062         this.processEvent("contextmenu", e);
6063     },
6064     
6065     processEvent : function(name, e)
6066     {
6067         if (name != 'touchstart' ) {
6068             this.fireEvent(name, e);    
6069         }
6070         
6071         var t = e.getTarget();
6072         
6073         var cell = Roo.get(t);
6074         
6075         if(!cell){
6076             return;
6077         }
6078         
6079         if(cell.findParent('tfoot', false, true)){
6080             return;
6081         }
6082         
6083         if(cell.findParent('thead', false, true)){
6084             
6085             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6086                 cell = Roo.get(t).findParent('th', false, true);
6087                 if (!cell) {
6088                     Roo.log("failed to find th in thead?");
6089                     Roo.log(e.getTarget());
6090                     return;
6091                 }
6092             }
6093             
6094             var cellIndex = cell.dom.cellIndex;
6095             
6096             var ename = name == 'touchstart' ? 'click' : name;
6097             this.fireEvent("header" + ename, this, cellIndex, e);
6098             
6099             return;
6100         }
6101         
6102         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6103             cell = Roo.get(t).findParent('td', false, true);
6104             if (!cell) {
6105                 Roo.log("failed to find th in tbody?");
6106                 Roo.log(e.getTarget());
6107                 return;
6108             }
6109         }
6110         
6111         var row = cell.findParent('tr', false, true);
6112         var cellIndex = cell.dom.cellIndex;
6113         var rowIndex = row.dom.rowIndex - 1;
6114         
6115         if(row !== false){
6116             
6117             this.fireEvent("row" + name, this, rowIndex, e);
6118             
6119             if(cell !== false){
6120             
6121                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6122             }
6123         }
6124         
6125     },
6126     
6127     onMouseover : function(e, el)
6128     {
6129         var cell = Roo.get(el);
6130         
6131         if(!cell){
6132             return;
6133         }
6134         
6135         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6136             cell = cell.findParent('td', false, true);
6137         }
6138         
6139         var row = cell.findParent('tr', false, true);
6140         var cellIndex = cell.dom.cellIndex;
6141         var rowIndex = row.dom.rowIndex - 1; // start from 0
6142         
6143         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6144         
6145     },
6146     
6147     onMouseout : function(e, el)
6148     {
6149         var cell = Roo.get(el);
6150         
6151         if(!cell){
6152             return;
6153         }
6154         
6155         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6156             cell = cell.findParent('td', false, true);
6157         }
6158         
6159         var row = cell.findParent('tr', false, true);
6160         var cellIndex = cell.dom.cellIndex;
6161         var rowIndex = row.dom.rowIndex - 1; // start from 0
6162         
6163         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6164         
6165     },
6166     
6167     onClick : function(e, el)
6168     {
6169         var cell = Roo.get(el);
6170         
6171         if(!cell || (!this.cellSelection && !this.rowSelection)){
6172             return;
6173         }
6174         
6175         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6176             cell = cell.findParent('td', false, true);
6177         }
6178         
6179         if(!cell || typeof(cell) == 'undefined'){
6180             return;
6181         }
6182         
6183         var row = cell.findParent('tr', false, true);
6184         
6185         if(!row || typeof(row) == 'undefined'){
6186             return;
6187         }
6188         
6189         var cellIndex = cell.dom.cellIndex;
6190         var rowIndex = this.getRowIndex(row);
6191         
6192         // why??? - should these not be based on SelectionModel?
6193         if(this.cellSelection){
6194             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6195         }
6196         
6197         if(this.rowSelection){
6198             this.fireEvent('rowclick', this, row, rowIndex, e);
6199         }
6200         
6201         
6202     },
6203         
6204     onDblClick : function(e,el)
6205     {
6206         var cell = Roo.get(el);
6207         
6208         if(!cell || (!this.cellSelection && !this.rowSelection)){
6209             return;
6210         }
6211         
6212         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6213             cell = cell.findParent('td', false, true);
6214         }
6215         
6216         if(!cell || typeof(cell) == 'undefined'){
6217             return;
6218         }
6219         
6220         var row = cell.findParent('tr', false, true);
6221         
6222         if(!row || typeof(row) == 'undefined'){
6223             return;
6224         }
6225         
6226         var cellIndex = cell.dom.cellIndex;
6227         var rowIndex = this.getRowIndex(row);
6228         
6229         if(this.cellSelection){
6230             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6231         }
6232         
6233         if(this.rowSelection){
6234             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6235         }
6236     },
6237     
6238     sort : function(e,el)
6239     {
6240         var col = Roo.get(el);
6241         
6242         if(!col.hasClass('sortable')){
6243             return;
6244         }
6245         
6246         var sort = col.attr('sort');
6247         var dir = 'ASC';
6248         
6249         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6250             dir = 'DESC';
6251         }
6252         
6253         this.store.sortInfo = {field : sort, direction : dir};
6254         
6255         if (this.footer) {
6256             Roo.log("calling footer first");
6257             this.footer.onClick('first');
6258         } else {
6259         
6260             this.store.load({ params : { start : 0 } });
6261         }
6262     },
6263     
6264     renderHeader : function()
6265     {
6266         var header = {
6267             tag: 'thead',
6268             cn : []
6269         };
6270         
6271         var cm = this.cm;
6272         this.totalWidth = 0;
6273         
6274         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6275             
6276             var config = cm.config[i];
6277             
6278             var c = {
6279                 tag: 'th',
6280                 style : '',
6281                 html: cm.getColumnHeader(i)
6282             };
6283             
6284             var hh = '';
6285             
6286             if(typeof(config.sortable) != 'undefined' && config.sortable){
6287                 c.cls = 'sortable';
6288                 c.html = '<i class="glyphicon"></i>' + c.html;
6289             }
6290             
6291             if(typeof(config.lgHeader) != 'undefined'){
6292                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6293             }
6294             
6295             if(typeof(config.mdHeader) != 'undefined'){
6296                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6297             }
6298             
6299             if(typeof(config.smHeader) != 'undefined'){
6300                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6301             }
6302             
6303             if(typeof(config.xsHeader) != 'undefined'){
6304                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6305             }
6306             
6307             if(hh.length){
6308                 c.html = hh;
6309             }
6310             
6311             if(typeof(config.tooltip) != 'undefined'){
6312                 c.tooltip = config.tooltip;
6313             }
6314             
6315             if(typeof(config.colspan) != 'undefined'){
6316                 c.colspan = config.colspan;
6317             }
6318             
6319             if(typeof(config.hidden) != 'undefined' && config.hidden){
6320                 c.style += ' display:none;';
6321             }
6322             
6323             if(typeof(config.dataIndex) != 'undefined'){
6324                 c.sort = config.dataIndex;
6325             }
6326             
6327            
6328             
6329             if(typeof(config.align) != 'undefined' && config.align.length){
6330                 c.style += ' text-align:' + config.align + ';';
6331             }
6332             
6333             if(typeof(config.width) != 'undefined'){
6334                 c.style += ' width:' + config.width + 'px;';
6335                 this.totalWidth += config.width;
6336             } else {
6337                 this.totalWidth += 100; // assume minimum of 100 per column?
6338             }
6339             
6340             if(typeof(config.cls) != 'undefined'){
6341                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6342             }
6343             
6344             ['xs','sm','md','lg'].map(function(size){
6345                 
6346                 if(typeof(config[size]) == 'undefined'){
6347                     return;
6348                 }
6349                 
6350                 if (!config[size]) { // 0 = hidden
6351                     c.cls += ' hidden-' + size;
6352                     return;
6353                 }
6354                 
6355                 c.cls += ' col-' + size + '-' + config[size];
6356
6357             });
6358             
6359             header.cn.push(c)
6360         }
6361         
6362         return header;
6363     },
6364     
6365     renderBody : function()
6366     {
6367         var body = {
6368             tag: 'tbody',
6369             cn : [
6370                 {
6371                     tag: 'tr',
6372                     cn : [
6373                         {
6374                             tag : 'td',
6375                             colspan :  this.cm.getColumnCount()
6376                         }
6377                     ]
6378                 }
6379             ]
6380         };
6381         
6382         return body;
6383     },
6384     
6385     renderFooter : function()
6386     {
6387         var footer = {
6388             tag: 'tfoot',
6389             cn : [
6390                 {
6391                     tag: 'tr',
6392                     cn : [
6393                         {
6394                             tag : 'td',
6395                             colspan :  this.cm.getColumnCount()
6396                         }
6397                     ]
6398                 }
6399             ]
6400         };
6401         
6402         return footer;
6403     },
6404     
6405     
6406     
6407     onLoad : function()
6408     {
6409 //        Roo.log('ds onload');
6410         this.clear();
6411         
6412         var _this = this;
6413         var cm = this.cm;
6414         var ds = this.store;
6415         
6416         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6417             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6418             if (_this.store.sortInfo) {
6419                     
6420                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6421                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6422                 }
6423                 
6424                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6425                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6426                 }
6427             }
6428         });
6429         
6430         var tbody =  this.mainBody;
6431               
6432         if(ds.getCount() > 0){
6433             ds.data.each(function(d,rowIndex){
6434                 var row =  this.renderRow(cm, ds, rowIndex);
6435                 
6436                 tbody.createChild(row);
6437                 
6438                 var _this = this;
6439                 
6440                 if(row.cellObjects.length){
6441                     Roo.each(row.cellObjects, function(r){
6442                         _this.renderCellObject(r);
6443                     })
6444                 }
6445                 
6446             }, this);
6447         }
6448         
6449         Roo.each(this.el.select('tbody td', true).elements, function(e){
6450             e.on('mouseover', _this.onMouseover, _this);
6451         });
6452         
6453         Roo.each(this.el.select('tbody td', true).elements, function(e){
6454             e.on('mouseout', _this.onMouseout, _this);
6455         });
6456         this.fireEvent('rowsrendered', this);
6457         //if(this.loadMask){
6458         //    this.maskEl.hide();
6459         //}
6460         
6461         this.autoSize();
6462     },
6463     
6464     
6465     onUpdate : function(ds,record)
6466     {
6467         this.refreshRow(record);
6468         this.autoSize();
6469     },
6470     
6471     onRemove : function(ds, record, index, isUpdate){
6472         if(isUpdate !== true){
6473             this.fireEvent("beforerowremoved", this, index, record);
6474         }
6475         var bt = this.mainBody.dom;
6476         
6477         var rows = this.el.select('tbody > tr', true).elements;
6478         
6479         if(typeof(rows[index]) != 'undefined'){
6480             bt.removeChild(rows[index].dom);
6481         }
6482         
6483 //        if(bt.rows[index]){
6484 //            bt.removeChild(bt.rows[index]);
6485 //        }
6486         
6487         if(isUpdate !== true){
6488             //this.stripeRows(index);
6489             //this.syncRowHeights(index, index);
6490             //this.layout();
6491             this.fireEvent("rowremoved", this, index, record);
6492         }
6493     },
6494     
6495     onAdd : function(ds, records, rowIndex)
6496     {
6497         //Roo.log('on Add called');
6498         // - note this does not handle multiple adding very well..
6499         var bt = this.mainBody.dom;
6500         for (var i =0 ; i < records.length;i++) {
6501             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6502             //Roo.log(records[i]);
6503             //Roo.log(this.store.getAt(rowIndex+i));
6504             this.insertRow(this.store, rowIndex + i, false);
6505             return;
6506         }
6507         
6508     },
6509     
6510     
6511     refreshRow : function(record){
6512         var ds = this.store, index;
6513         if(typeof record == 'number'){
6514             index = record;
6515             record = ds.getAt(index);
6516         }else{
6517             index = ds.indexOf(record);
6518         }
6519         this.insertRow(ds, index, true);
6520         this.autoSize();
6521         this.onRemove(ds, record, index+1, true);
6522         this.autoSize();
6523         //this.syncRowHeights(index, index);
6524         //this.layout();
6525         this.fireEvent("rowupdated", this, index, record);
6526     },
6527     
6528     insertRow : function(dm, rowIndex, isUpdate){
6529         
6530         if(!isUpdate){
6531             this.fireEvent("beforerowsinserted", this, rowIndex);
6532         }
6533             //var s = this.getScrollState();
6534         var row = this.renderRow(this.cm, this.store, rowIndex);
6535         // insert before rowIndex..
6536         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6537         
6538         var _this = this;
6539                 
6540         if(row.cellObjects.length){
6541             Roo.each(row.cellObjects, function(r){
6542                 _this.renderCellObject(r);
6543             })
6544         }
6545             
6546         if(!isUpdate){
6547             this.fireEvent("rowsinserted", this, rowIndex);
6548             //this.syncRowHeights(firstRow, lastRow);
6549             //this.stripeRows(firstRow);
6550             //this.layout();
6551         }
6552         
6553     },
6554     
6555     
6556     getRowDom : function(rowIndex)
6557     {
6558         var rows = this.el.select('tbody > tr', true).elements;
6559         
6560         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6561         
6562     },
6563     // returns the object tree for a tr..
6564   
6565     
6566     renderRow : function(cm, ds, rowIndex) 
6567     {
6568         
6569         var d = ds.getAt(rowIndex);
6570         
6571         var row = {
6572             tag : 'tr',
6573             cn : []
6574         };
6575             
6576         var cellObjects = [];
6577         
6578         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6579             var config = cm.config[i];
6580             
6581             var renderer = cm.getRenderer(i);
6582             var value = '';
6583             var id = false;
6584             
6585             if(typeof(renderer) !== 'undefined'){
6586                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6587             }
6588             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6589             // and are rendered into the cells after the row is rendered - using the id for the element.
6590             
6591             if(typeof(value) === 'object'){
6592                 id = Roo.id();
6593                 cellObjects.push({
6594                     container : id,
6595                     cfg : value 
6596                 })
6597             }
6598             
6599             var rowcfg = {
6600                 record: d,
6601                 rowIndex : rowIndex,
6602                 colIndex : i,
6603                 rowClass : ''
6604             };
6605
6606             this.fireEvent('rowclass', this, rowcfg);
6607             
6608             var td = {
6609                 tag: 'td',
6610                 cls : rowcfg.rowClass,
6611                 style: '',
6612                 html: (typeof(value) === 'object') ? '' : value
6613             };
6614             
6615             if (id) {
6616                 td.id = id;
6617             }
6618             
6619             if(typeof(config.colspan) != 'undefined'){
6620                 td.colspan = config.colspan;
6621             }
6622             
6623             if(typeof(config.hidden) != 'undefined' && config.hidden){
6624                 td.style += ' display:none;';
6625             }
6626             
6627             if(typeof(config.align) != 'undefined' && config.align.length){
6628                 td.style += ' text-align:' + config.align + ';';
6629             }
6630             
6631             if(typeof(config.width) != 'undefined'){
6632                 td.style += ' width:' +  config.width + 'px;';
6633             }
6634             
6635             if(typeof(config.cursor) != 'undefined'){
6636                 td.style += ' cursor:' +  config.cursor + ';';
6637             }
6638             
6639             if(typeof(config.cls) != 'undefined'){
6640                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6641             }
6642             
6643             ['xs','sm','md','lg'].map(function(size){
6644                 
6645                 if(typeof(config[size]) == 'undefined'){
6646                     return;
6647                 }
6648                 
6649                 if (!config[size]) { // 0 = hidden
6650                     td.cls += ' hidden-' + size;
6651                     return;
6652                 }
6653                 
6654                 td.cls += ' col-' + size + '-' + config[size];
6655
6656             });
6657              
6658             row.cn.push(td);
6659            
6660         }
6661         
6662         row.cellObjects = cellObjects;
6663         
6664         return row;
6665           
6666     },
6667     
6668     
6669     
6670     onBeforeLoad : function()
6671     {
6672         //Roo.log('ds onBeforeLoad');
6673         
6674         //this.clear();
6675         
6676         //if(this.loadMask){
6677         //    this.maskEl.show();
6678         //}
6679     },
6680      /**
6681      * Remove all rows
6682      */
6683     clear : function()
6684     {
6685         this.el.select('tbody', true).first().dom.innerHTML = '';
6686     },
6687     /**
6688      * Show or hide a row.
6689      * @param {Number} rowIndex to show or hide
6690      * @param {Boolean} state hide
6691      */
6692     setRowVisibility : function(rowIndex, state)
6693     {
6694         var bt = this.mainBody.dom;
6695         
6696         var rows = this.el.select('tbody > tr', true).elements;
6697         
6698         if(typeof(rows[rowIndex]) == 'undefined'){
6699             return;
6700         }
6701         rows[rowIndex].dom.style.display = state ? '' : 'none';
6702     },
6703     
6704     
6705     getSelectionModel : function(){
6706         if(!this.selModel){
6707             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6708         }
6709         return this.selModel;
6710     },
6711     /*
6712      * Render the Roo.bootstrap object from renderder
6713      */
6714     renderCellObject : function(r)
6715     {
6716         var _this = this;
6717         
6718         var t = r.cfg.render(r.container);
6719         
6720         if(r.cfg.cn){
6721             Roo.each(r.cfg.cn, function(c){
6722                 var child = {
6723                     container: t.getChildContainer(),
6724                     cfg: c
6725                 };
6726                 _this.renderCellObject(child);
6727             })
6728         }
6729     },
6730     
6731     getRowIndex : function(row)
6732     {
6733         var rowIndex = -1;
6734         
6735         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6736             if(el != row){
6737                 return;
6738             }
6739             
6740             rowIndex = index;
6741         });
6742         
6743         return rowIndex;
6744     },
6745      /**
6746      * Returns the grid's underlying element = used by panel.Grid
6747      * @return {Element} The element
6748      */
6749     getGridEl : function(){
6750         return this.el;
6751     },
6752      /**
6753      * Forces a resize - used by panel.Grid
6754      * @return {Element} The element
6755      */
6756     autoSize : function()
6757     {
6758         //var ctr = Roo.get(this.container.dom.parentElement);
6759         var ctr = Roo.get(this.el.dom);
6760         
6761         var thd = this.getGridEl().select('thead',true).first();
6762         var tbd = this.getGridEl().select('tbody', true).first();
6763         var tfd = this.getGridEl().select('tfoot', true).first();
6764         
6765         var cw = ctr.getWidth();
6766         
6767         if (tbd) {
6768             
6769             tbd.setSize(ctr.getWidth(),
6770                         ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6771             );
6772             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6773             cw -= barsize;
6774         }
6775         cw = Math.max(cw, this.totalWidth);
6776         this.getGridEl().select('tr',true).setWidth(cw);
6777         // resize 'expandable coloumn?
6778         
6779         return; // we doe not have a view in this design..
6780         
6781     },
6782     onBodyScroll: function()
6783     {
6784         
6785         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6786         this.mainHead.setStyle({
6787                     'position' : 'relative',
6788                     'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6789         });
6790         
6791         
6792     }
6793 });
6794
6795  
6796
6797  /*
6798  * - LGPL
6799  *
6800  * table cell
6801  * 
6802  */
6803
6804 /**
6805  * @class Roo.bootstrap.TableCell
6806  * @extends Roo.bootstrap.Component
6807  * Bootstrap TableCell class
6808  * @cfg {String} html cell contain text
6809  * @cfg {String} cls cell class
6810  * @cfg {String} tag cell tag (td|th) default td
6811  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6812  * @cfg {String} align Aligns the content in a cell
6813  * @cfg {String} axis Categorizes cells
6814  * @cfg {String} bgcolor Specifies the background color of a cell
6815  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6816  * @cfg {Number} colspan Specifies the number of columns a cell should span
6817  * @cfg {String} headers Specifies one or more header cells a cell is related to
6818  * @cfg {Number} height Sets the height of a cell
6819  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6820  * @cfg {Number} rowspan Sets the number of rows a cell should span
6821  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6822  * @cfg {String} valign Vertical aligns the content in a cell
6823  * @cfg {Number} width Specifies the width of a cell
6824  * 
6825  * @constructor
6826  * Create a new TableCell
6827  * @param {Object} config The config object
6828  */
6829
6830 Roo.bootstrap.TableCell = function(config){
6831     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6832 };
6833
6834 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6835     
6836     html: false,
6837     cls: false,
6838     tag: false,
6839     abbr: false,
6840     align: false,
6841     axis: false,
6842     bgcolor: false,
6843     charoff: false,
6844     colspan: false,
6845     headers: false,
6846     height: false,
6847     nowrap: false,
6848     rowspan: false,
6849     scope: false,
6850     valign: false,
6851     width: false,
6852     
6853     
6854     getAutoCreate : function(){
6855         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6856         
6857         cfg = {
6858             tag: 'td'
6859         };
6860         
6861         if(this.tag){
6862             cfg.tag = this.tag;
6863         }
6864         
6865         if (this.html) {
6866             cfg.html=this.html
6867         }
6868         if (this.cls) {
6869             cfg.cls=this.cls
6870         }
6871         if (this.abbr) {
6872             cfg.abbr=this.abbr
6873         }
6874         if (this.align) {
6875             cfg.align=this.align
6876         }
6877         if (this.axis) {
6878             cfg.axis=this.axis
6879         }
6880         if (this.bgcolor) {
6881             cfg.bgcolor=this.bgcolor
6882         }
6883         if (this.charoff) {
6884             cfg.charoff=this.charoff
6885         }
6886         if (this.colspan) {
6887             cfg.colspan=this.colspan
6888         }
6889         if (this.headers) {
6890             cfg.headers=this.headers
6891         }
6892         if (this.height) {
6893             cfg.height=this.height
6894         }
6895         if (this.nowrap) {
6896             cfg.nowrap=this.nowrap
6897         }
6898         if (this.rowspan) {
6899             cfg.rowspan=this.rowspan
6900         }
6901         if (this.scope) {
6902             cfg.scope=this.scope
6903         }
6904         if (this.valign) {
6905             cfg.valign=this.valign
6906         }
6907         if (this.width) {
6908             cfg.width=this.width
6909         }
6910         
6911         
6912         return cfg;
6913     }
6914    
6915 });
6916
6917  
6918
6919  /*
6920  * - LGPL
6921  *
6922  * table row
6923  * 
6924  */
6925
6926 /**
6927  * @class Roo.bootstrap.TableRow
6928  * @extends Roo.bootstrap.Component
6929  * Bootstrap TableRow class
6930  * @cfg {String} cls row class
6931  * @cfg {String} align Aligns the content in a table row
6932  * @cfg {String} bgcolor Specifies a background color for a table row
6933  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6934  * @cfg {String} valign Vertical aligns the content in a table row
6935  * 
6936  * @constructor
6937  * Create a new TableRow
6938  * @param {Object} config The config object
6939  */
6940
6941 Roo.bootstrap.TableRow = function(config){
6942     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6943 };
6944
6945 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6946     
6947     cls: false,
6948     align: false,
6949     bgcolor: false,
6950     charoff: false,
6951     valign: false,
6952     
6953     getAutoCreate : function(){
6954         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6955         
6956         cfg = {
6957             tag: 'tr'
6958         };
6959             
6960         if(this.cls){
6961             cfg.cls = this.cls;
6962         }
6963         if(this.align){
6964             cfg.align = this.align;
6965         }
6966         if(this.bgcolor){
6967             cfg.bgcolor = this.bgcolor;
6968         }
6969         if(this.charoff){
6970             cfg.charoff = this.charoff;
6971         }
6972         if(this.valign){
6973             cfg.valign = this.valign;
6974         }
6975         
6976         return cfg;
6977     }
6978    
6979 });
6980
6981  
6982
6983  /*
6984  * - LGPL
6985  *
6986  * table body
6987  * 
6988  */
6989
6990 /**
6991  * @class Roo.bootstrap.TableBody
6992  * @extends Roo.bootstrap.Component
6993  * Bootstrap TableBody class
6994  * @cfg {String} cls element class
6995  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6996  * @cfg {String} align Aligns the content inside the element
6997  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6998  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6999  * 
7000  * @constructor
7001  * Create a new TableBody
7002  * @param {Object} config The config object
7003  */
7004
7005 Roo.bootstrap.TableBody = function(config){
7006     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7007 };
7008
7009 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7010     
7011     cls: false,
7012     tag: false,
7013     align: false,
7014     charoff: false,
7015     valign: false,
7016     
7017     getAutoCreate : function(){
7018         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7019         
7020         cfg = {
7021             tag: 'tbody'
7022         };
7023             
7024         if (this.cls) {
7025             cfg.cls=this.cls
7026         }
7027         if(this.tag){
7028             cfg.tag = this.tag;
7029         }
7030         
7031         if(this.align){
7032             cfg.align = this.align;
7033         }
7034         if(this.charoff){
7035             cfg.charoff = this.charoff;
7036         }
7037         if(this.valign){
7038             cfg.valign = this.valign;
7039         }
7040         
7041         return cfg;
7042     }
7043     
7044     
7045 //    initEvents : function()
7046 //    {
7047 //        
7048 //        if(!this.store){
7049 //            return;
7050 //        }
7051 //        
7052 //        this.store = Roo.factory(this.store, Roo.data);
7053 //        this.store.on('load', this.onLoad, this);
7054 //        
7055 //        this.store.load();
7056 //        
7057 //    },
7058 //    
7059 //    onLoad: function () 
7060 //    {   
7061 //        this.fireEvent('load', this);
7062 //    }
7063 //    
7064 //   
7065 });
7066
7067  
7068
7069  /*
7070  * Based on:
7071  * Ext JS Library 1.1.1
7072  * Copyright(c) 2006-2007, Ext JS, LLC.
7073  *
7074  * Originally Released Under LGPL - original licence link has changed is not relivant.
7075  *
7076  * Fork - LGPL
7077  * <script type="text/javascript">
7078  */
7079
7080 // as we use this in bootstrap.
7081 Roo.namespace('Roo.form');
7082  /**
7083  * @class Roo.form.Action
7084  * Internal Class used to handle form actions
7085  * @constructor
7086  * @param {Roo.form.BasicForm} el The form element or its id
7087  * @param {Object} config Configuration options
7088  */
7089
7090  
7091  
7092 // define the action interface
7093 Roo.form.Action = function(form, options){
7094     this.form = form;
7095     this.options = options || {};
7096 };
7097 /**
7098  * Client Validation Failed
7099  * @const 
7100  */
7101 Roo.form.Action.CLIENT_INVALID = 'client';
7102 /**
7103  * Server Validation Failed
7104  * @const 
7105  */
7106 Roo.form.Action.SERVER_INVALID = 'server';
7107  /**
7108  * Connect to Server Failed
7109  * @const 
7110  */
7111 Roo.form.Action.CONNECT_FAILURE = 'connect';
7112 /**
7113  * Reading Data from Server Failed
7114  * @const 
7115  */
7116 Roo.form.Action.LOAD_FAILURE = 'load';
7117
7118 Roo.form.Action.prototype = {
7119     type : 'default',
7120     failureType : undefined,
7121     response : undefined,
7122     result : undefined,
7123
7124     // interface method
7125     run : function(options){
7126
7127     },
7128
7129     // interface method
7130     success : function(response){
7131
7132     },
7133
7134     // interface method
7135     handleResponse : function(response){
7136
7137     },
7138
7139     // default connection failure
7140     failure : function(response){
7141         
7142         this.response = response;
7143         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7144         this.form.afterAction(this, false);
7145     },
7146
7147     processResponse : function(response){
7148         this.response = response;
7149         if(!response.responseText){
7150             return true;
7151         }
7152         this.result = this.handleResponse(response);
7153         return this.result;
7154     },
7155
7156     // utility functions used internally
7157     getUrl : function(appendParams){
7158         var url = this.options.url || this.form.url || this.form.el.dom.action;
7159         if(appendParams){
7160             var p = this.getParams();
7161             if(p){
7162                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7163             }
7164         }
7165         return url;
7166     },
7167
7168     getMethod : function(){
7169         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7170     },
7171
7172     getParams : function(){
7173         var bp = this.form.baseParams;
7174         var p = this.options.params;
7175         if(p){
7176             if(typeof p == "object"){
7177                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7178             }else if(typeof p == 'string' && bp){
7179                 p += '&' + Roo.urlEncode(bp);
7180             }
7181         }else if(bp){
7182             p = Roo.urlEncode(bp);
7183         }
7184         return p;
7185     },
7186
7187     createCallback : function(){
7188         return {
7189             success: this.success,
7190             failure: this.failure,
7191             scope: this,
7192             timeout: (this.form.timeout*1000),
7193             upload: this.form.fileUpload ? this.success : undefined
7194         };
7195     }
7196 };
7197
7198 Roo.form.Action.Submit = function(form, options){
7199     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7200 };
7201
7202 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7203     type : 'submit',
7204
7205     haveProgress : false,
7206     uploadComplete : false,
7207     
7208     // uploadProgress indicator.
7209     uploadProgress : function()
7210     {
7211         if (!this.form.progressUrl) {
7212             return;
7213         }
7214         
7215         if (!this.haveProgress) {
7216             Roo.MessageBox.progress("Uploading", "Uploading");
7217         }
7218         if (this.uploadComplete) {
7219            Roo.MessageBox.hide();
7220            return;
7221         }
7222         
7223         this.haveProgress = true;
7224    
7225         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7226         
7227         var c = new Roo.data.Connection();
7228         c.request({
7229             url : this.form.progressUrl,
7230             params: {
7231                 id : uid
7232             },
7233             method: 'GET',
7234             success : function(req){
7235                //console.log(data);
7236                 var rdata = false;
7237                 var edata;
7238                 try  {
7239                    rdata = Roo.decode(req.responseText)
7240                 } catch (e) {
7241                     Roo.log("Invalid data from server..");
7242                     Roo.log(edata);
7243                     return;
7244                 }
7245                 if (!rdata || !rdata.success) {
7246                     Roo.log(rdata);
7247                     Roo.MessageBox.alert(Roo.encode(rdata));
7248                     return;
7249                 }
7250                 var data = rdata.data;
7251                 
7252                 if (this.uploadComplete) {
7253                    Roo.MessageBox.hide();
7254                    return;
7255                 }
7256                    
7257                 if (data){
7258                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7259                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7260                     );
7261                 }
7262                 this.uploadProgress.defer(2000,this);
7263             },
7264        
7265             failure: function(data) {
7266                 Roo.log('progress url failed ');
7267                 Roo.log(data);
7268             },
7269             scope : this
7270         });
7271            
7272     },
7273     
7274     
7275     run : function()
7276     {
7277         // run get Values on the form, so it syncs any secondary forms.
7278         this.form.getValues();
7279         
7280         var o = this.options;
7281         var method = this.getMethod();
7282         var isPost = method == 'POST';
7283         if(o.clientValidation === false || this.form.isValid()){
7284             
7285             if (this.form.progressUrl) {
7286                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7287                     (new Date() * 1) + '' + Math.random());
7288                     
7289             } 
7290             
7291             
7292             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7293                 form:this.form.el.dom,
7294                 url:this.getUrl(!isPost),
7295                 method: method,
7296                 params:isPost ? this.getParams() : null,
7297                 isUpload: this.form.fileUpload
7298             }));
7299             
7300             this.uploadProgress();
7301
7302         }else if (o.clientValidation !== false){ // client validation failed
7303             this.failureType = Roo.form.Action.CLIENT_INVALID;
7304             this.form.afterAction(this, false);
7305         }
7306     },
7307
7308     success : function(response)
7309     {
7310         this.uploadComplete= true;
7311         if (this.haveProgress) {
7312             Roo.MessageBox.hide();
7313         }
7314         
7315         
7316         var result = this.processResponse(response);
7317         if(result === true || result.success){
7318             this.form.afterAction(this, true);
7319             return;
7320         }
7321         if(result.errors){
7322             this.form.markInvalid(result.errors);
7323             this.failureType = Roo.form.Action.SERVER_INVALID;
7324         }
7325         this.form.afterAction(this, false);
7326     },
7327     failure : function(response)
7328     {
7329         this.uploadComplete= true;
7330         if (this.haveProgress) {
7331             Roo.MessageBox.hide();
7332         }
7333         
7334         this.response = response;
7335         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7336         this.form.afterAction(this, false);
7337     },
7338     
7339     handleResponse : function(response){
7340         if(this.form.errorReader){
7341             var rs = this.form.errorReader.read(response);
7342             var errors = [];
7343             if(rs.records){
7344                 for(var i = 0, len = rs.records.length; i < len; i++) {
7345                     var r = rs.records[i];
7346                     errors[i] = r.data;
7347                 }
7348             }
7349             if(errors.length < 1){
7350                 errors = null;
7351             }
7352             return {
7353                 success : rs.success,
7354                 errors : errors
7355             };
7356         }
7357         var ret = false;
7358         try {
7359             ret = Roo.decode(response.responseText);
7360         } catch (e) {
7361             ret = {
7362                 success: false,
7363                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7364                 errors : []
7365             };
7366         }
7367         return ret;
7368         
7369     }
7370 });
7371
7372
7373 Roo.form.Action.Load = function(form, options){
7374     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7375     this.reader = this.form.reader;
7376 };
7377
7378 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7379     type : 'load',
7380
7381     run : function(){
7382         
7383         Roo.Ajax.request(Roo.apply(
7384                 this.createCallback(), {
7385                     method:this.getMethod(),
7386                     url:this.getUrl(false),
7387                     params:this.getParams()
7388         }));
7389     },
7390
7391     success : function(response){
7392         
7393         var result = this.processResponse(response);
7394         if(result === true || !result.success || !result.data){
7395             this.failureType = Roo.form.Action.LOAD_FAILURE;
7396             this.form.afterAction(this, false);
7397             return;
7398         }
7399         this.form.clearInvalid();
7400         this.form.setValues(result.data);
7401         this.form.afterAction(this, true);
7402     },
7403
7404     handleResponse : function(response){
7405         if(this.form.reader){
7406             var rs = this.form.reader.read(response);
7407             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7408             return {
7409                 success : rs.success,
7410                 data : data
7411             };
7412         }
7413         return Roo.decode(response.responseText);
7414     }
7415 });
7416
7417 Roo.form.Action.ACTION_TYPES = {
7418     'load' : Roo.form.Action.Load,
7419     'submit' : Roo.form.Action.Submit
7420 };/*
7421  * - LGPL
7422  *
7423  * form
7424  *
7425  */
7426
7427 /**
7428  * @class Roo.bootstrap.Form
7429  * @extends Roo.bootstrap.Component
7430  * Bootstrap Form class
7431  * @cfg {String} method  GET | POST (default POST)
7432  * @cfg {String} labelAlign top | left (default top)
7433  * @cfg {String} align left  | right - for navbars
7434  * @cfg {Boolean} loadMask load mask when submit (default true)
7435
7436  *
7437  * @constructor
7438  * Create a new Form
7439  * @param {Object} config The config object
7440  */
7441
7442
7443 Roo.bootstrap.Form = function(config){
7444     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7445     
7446     Roo.bootstrap.Form.popover.apply();
7447     
7448     this.addEvents({
7449         /**
7450          * @event clientvalidation
7451          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7452          * @param {Form} this
7453          * @param {Boolean} valid true if the form has passed client-side validation
7454          */
7455         clientvalidation: true,
7456         /**
7457          * @event beforeaction
7458          * Fires before any action is performed. Return false to cancel the action.
7459          * @param {Form} this
7460          * @param {Action} action The action to be performed
7461          */
7462         beforeaction: true,
7463         /**
7464          * @event actionfailed
7465          * Fires when an action fails.
7466          * @param {Form} this
7467          * @param {Action} action The action that failed
7468          */
7469         actionfailed : true,
7470         /**
7471          * @event actioncomplete
7472          * Fires when an action is completed.
7473          * @param {Form} this
7474          * @param {Action} action The action that completed
7475          */
7476         actioncomplete : true
7477     });
7478
7479 };
7480
7481 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7482
7483      /**
7484      * @cfg {String} method
7485      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7486      */
7487     method : 'POST',
7488     /**
7489      * @cfg {String} url
7490      * The URL to use for form actions if one isn't supplied in the action options.
7491      */
7492     /**
7493      * @cfg {Boolean} fileUpload
7494      * Set to true if this form is a file upload.
7495      */
7496
7497     /**
7498      * @cfg {Object} baseParams
7499      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7500      */
7501
7502     /**
7503      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7504      */
7505     timeout: 30,
7506     /**
7507      * @cfg {Sting} align (left|right) for navbar forms
7508      */
7509     align : 'left',
7510
7511     // private
7512     activeAction : null,
7513
7514     /**
7515      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7516      * element by passing it or its id or mask the form itself by passing in true.
7517      * @type Mixed
7518      */
7519     waitMsgTarget : false,
7520
7521     loadMask : true,
7522     
7523     /**
7524      * @cfg {Boolean} errorMask (true|false) default false
7525      */
7526     errorMask : false,
7527
7528     getAutoCreate : function(){
7529
7530         var cfg = {
7531             tag: 'form',
7532             method : this.method || 'POST',
7533             id : this.id || Roo.id(),
7534             cls : ''
7535         };
7536         if (this.parent().xtype.match(/^Nav/)) {
7537             cfg.cls = 'navbar-form navbar-' + this.align;
7538
7539         }
7540
7541         if (this.labelAlign == 'left' ) {
7542             cfg.cls += ' form-horizontal';
7543         }
7544
7545
7546         return cfg;
7547     },
7548     initEvents : function()
7549     {
7550         this.el.on('submit', this.onSubmit, this);
7551         // this was added as random key presses on the form where triggering form submit.
7552         this.el.on('keypress', function(e) {
7553             if (e.getCharCode() != 13) {
7554                 return true;
7555             }
7556             // we might need to allow it for textareas.. and some other items.
7557             // check e.getTarget().
7558
7559             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7560                 return true;
7561             }
7562
7563             Roo.log("keypress blocked");
7564
7565             e.preventDefault();
7566             return false;
7567         });
7568         
7569     },
7570     // private
7571     onSubmit : function(e){
7572         e.stopEvent();
7573     },
7574
7575      /**
7576      * Returns true if client-side validation on the form is successful.
7577      * @return Boolean
7578      */
7579     isValid : function(){
7580         var items = this.getItems();
7581         var valid = true;
7582         var target = false;
7583         
7584         items.each(function(f){
7585             
7586             if(f.validate()){
7587                 return;
7588             }
7589             valid = false;
7590
7591             if(!target && f.el.isVisible(true)){
7592                 target = f;
7593             }
7594            
7595         });
7596         
7597         if(this.errorMask && !valid){
7598             Roo.bootstrap.Form.popover.mask(this, target);
7599         }
7600         
7601         return valid;
7602     },
7603     
7604     /**
7605      * Returns true if any fields in this form have changed since their original load.
7606      * @return Boolean
7607      */
7608     isDirty : function(){
7609         var dirty = false;
7610         var items = this.getItems();
7611         items.each(function(f){
7612            if(f.isDirty()){
7613                dirty = true;
7614                return false;
7615            }
7616            return true;
7617         });
7618         return dirty;
7619     },
7620      /**
7621      * Performs a predefined action (submit or load) or custom actions you define on this form.
7622      * @param {String} actionName The name of the action type
7623      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7624      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7625      * accept other config options):
7626      * <pre>
7627 Property          Type             Description
7628 ----------------  ---------------  ----------------------------------------------------------------------------------
7629 url               String           The url for the action (defaults to the form's url)
7630 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7631 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7632 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7633                                    validate the form on the client (defaults to false)
7634      * </pre>
7635      * @return {BasicForm} this
7636      */
7637     doAction : function(action, options){
7638         if(typeof action == 'string'){
7639             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7640         }
7641         if(this.fireEvent('beforeaction', this, action) !== false){
7642             this.beforeAction(action);
7643             action.run.defer(100, action);
7644         }
7645         return this;
7646     },
7647
7648     // private
7649     beforeAction : function(action){
7650         var o = action.options;
7651
7652         if(this.loadMask){
7653             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7654         }
7655         // not really supported yet.. ??
7656
7657         //if(this.waitMsgTarget === true){
7658         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7659         //}else if(this.waitMsgTarget){
7660         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7661         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7662         //}else {
7663         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7664        // }
7665
7666     },
7667
7668     // private
7669     afterAction : function(action, success){
7670         this.activeAction = null;
7671         var o = action.options;
7672
7673         //if(this.waitMsgTarget === true){
7674             this.el.unmask();
7675         //}else if(this.waitMsgTarget){
7676         //    this.waitMsgTarget.unmask();
7677         //}else{
7678         //    Roo.MessageBox.updateProgress(1);
7679         //    Roo.MessageBox.hide();
7680        // }
7681         //
7682         if(success){
7683             if(o.reset){
7684                 this.reset();
7685             }
7686             Roo.callback(o.success, o.scope, [this, action]);
7687             this.fireEvent('actioncomplete', this, action);
7688
7689         }else{
7690
7691             // failure condition..
7692             // we have a scenario where updates need confirming.
7693             // eg. if a locking scenario exists..
7694             // we look for { errors : { needs_confirm : true }} in the response.
7695             if (
7696                 (typeof(action.result) != 'undefined')  &&
7697                 (typeof(action.result.errors) != 'undefined')  &&
7698                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7699            ){
7700                 var _t = this;
7701                 Roo.log("not supported yet");
7702                  /*
7703
7704                 Roo.MessageBox.confirm(
7705                     "Change requires confirmation",
7706                     action.result.errorMsg,
7707                     function(r) {
7708                         if (r != 'yes') {
7709                             return;
7710                         }
7711                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7712                     }
7713
7714                 );
7715                 */
7716
7717
7718                 return;
7719             }
7720
7721             Roo.callback(o.failure, o.scope, [this, action]);
7722             // show an error message if no failed handler is set..
7723             if (!this.hasListener('actionfailed')) {
7724                 Roo.log("need to add dialog support");
7725                 /*
7726                 Roo.MessageBox.alert("Error",
7727                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7728                         action.result.errorMsg :
7729                         "Saving Failed, please check your entries or try again"
7730                 );
7731                 */
7732             }
7733
7734             this.fireEvent('actionfailed', this, action);
7735         }
7736
7737     },
7738     /**
7739      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7740      * @param {String} id The value to search for
7741      * @return Field
7742      */
7743     findField : function(id){
7744         var items = this.getItems();
7745         var field = items.get(id);
7746         if(!field){
7747              items.each(function(f){
7748                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7749                     field = f;
7750                     return false;
7751                 }
7752                 return true;
7753             });
7754         }
7755         return field || null;
7756     },
7757      /**
7758      * Mark fields in this form invalid in bulk.
7759      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7760      * @return {BasicForm} this
7761      */
7762     markInvalid : function(errors){
7763         if(errors instanceof Array){
7764             for(var i = 0, len = errors.length; i < len; i++){
7765                 var fieldError = errors[i];
7766                 var f = this.findField(fieldError.id);
7767                 if(f){
7768                     f.markInvalid(fieldError.msg);
7769                 }
7770             }
7771         }else{
7772             var field, id;
7773             for(id in errors){
7774                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7775                     field.markInvalid(errors[id]);
7776                 }
7777             }
7778         }
7779         //Roo.each(this.childForms || [], function (f) {
7780         //    f.markInvalid(errors);
7781         //});
7782
7783         return this;
7784     },
7785
7786     /**
7787      * Set values for fields in this form in bulk.
7788      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7789      * @return {BasicForm} this
7790      */
7791     setValues : function(values){
7792         if(values instanceof Array){ // array of objects
7793             for(var i = 0, len = values.length; i < len; i++){
7794                 var v = values[i];
7795                 var f = this.findField(v.id);
7796                 if(f){
7797                     f.setValue(v.value);
7798                     if(this.trackResetOnLoad){
7799                         f.originalValue = f.getValue();
7800                     }
7801                 }
7802             }
7803         }else{ // object hash
7804             var field, id;
7805             for(id in values){
7806                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7807
7808                     if (field.setFromData &&
7809                         field.valueField &&
7810                         field.displayField &&
7811                         // combos' with local stores can
7812                         // be queried via setValue()
7813                         // to set their value..
7814                         (field.store && !field.store.isLocal)
7815                         ) {
7816                         // it's a combo
7817                         var sd = { };
7818                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7819                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7820                         field.setFromData(sd);
7821
7822                     } else {
7823                         field.setValue(values[id]);
7824                     }
7825
7826
7827                     if(this.trackResetOnLoad){
7828                         field.originalValue = field.getValue();
7829                     }
7830                 }
7831             }
7832         }
7833
7834         //Roo.each(this.childForms || [], function (f) {
7835         //    f.setValues(values);
7836         //});
7837
7838         return this;
7839     },
7840
7841     /**
7842      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7843      * they are returned as an array.
7844      * @param {Boolean} asString
7845      * @return {Object}
7846      */
7847     getValues : function(asString){
7848         //if (this.childForms) {
7849             // copy values from the child forms
7850         //    Roo.each(this.childForms, function (f) {
7851         //        this.setValues(f.getValues());
7852         //    }, this);
7853         //}
7854
7855
7856
7857         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7858         if(asString === true){
7859             return fs;
7860         }
7861         return Roo.urlDecode(fs);
7862     },
7863
7864     /**
7865      * Returns the fields in this form as an object with key/value pairs.
7866      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7867      * @return {Object}
7868      */
7869     getFieldValues : function(with_hidden)
7870     {
7871         var items = this.getItems();
7872         var ret = {};
7873         items.each(function(f){
7874             if (!f.getName()) {
7875                 return;
7876             }
7877             var v = f.getValue();
7878             if (f.inputType =='radio') {
7879                 if (typeof(ret[f.getName()]) == 'undefined') {
7880                     ret[f.getName()] = ''; // empty..
7881                 }
7882
7883                 if (!f.el.dom.checked) {
7884                     return;
7885
7886                 }
7887                 v = f.el.dom.value;
7888
7889             }
7890
7891             // not sure if this supported any more..
7892             if ((typeof(v) == 'object') && f.getRawValue) {
7893                 v = f.getRawValue() ; // dates..
7894             }
7895             // combo boxes where name != hiddenName...
7896             if (f.name !== false && f.name != '' && f.name != f.getName()) {
7897                 ret[f.name] = f.getRawValue();
7898             }
7899             ret[f.getName()] = v;
7900         });
7901
7902         return ret;
7903     },
7904
7905     /**
7906      * Clears all invalid messages in this form.
7907      * @return {BasicForm} this
7908      */
7909     clearInvalid : function(){
7910         var items = this.getItems();
7911
7912         items.each(function(f){
7913            f.clearInvalid();
7914         });
7915
7916
7917
7918         return this;
7919     },
7920
7921     /**
7922      * Resets this form.
7923      * @return {BasicForm} this
7924      */
7925     reset : function(){
7926         var items = this.getItems();
7927         items.each(function(f){
7928             f.reset();
7929         });
7930
7931         Roo.each(this.childForms || [], function (f) {
7932             f.reset();
7933         });
7934
7935
7936         return this;
7937     },
7938     getItems : function()
7939     {
7940         var r=new Roo.util.MixedCollection(false, function(o){
7941             return o.id || (o.id = Roo.id());
7942         });
7943         var iter = function(el) {
7944             if (el.inputEl) {
7945                 r.add(el);
7946             }
7947             if (!el.items) {
7948                 return;
7949             }
7950             Roo.each(el.items,function(e) {
7951                 iter(e);
7952             });
7953
7954
7955         };
7956
7957         iter(this);
7958         return r;
7959
7960
7961
7962
7963     }
7964
7965 });
7966
7967 Roo.apply(Roo.bootstrap.Form, {
7968     
7969     popover : {
7970         
7971         padding : 5,
7972         
7973         isApplied : false,
7974         
7975         isMasked : false,
7976         
7977         form : false,
7978         
7979         target : false,
7980         
7981         toolTip : false,
7982         
7983         intervalID : false,
7984         
7985         maskEl : false,
7986         
7987         apply : function()
7988         {
7989             if(this.isApplied){
7990                 return;
7991             }
7992             
7993             this.maskEl = {
7994                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
7995                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
7996                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
7997                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
7998             };
7999             
8000             this.maskEl.top.enableDisplayMode("block");
8001             this.maskEl.left.enableDisplayMode("block");
8002             this.maskEl.bottom.enableDisplayMode("block");
8003             this.maskEl.right.enableDisplayMode("block");
8004             
8005             this.toolTip = new Roo.bootstrap.Tooltip({
8006                 cls : 'roo-form-error-popover',
8007                 alignment : {
8008                     'left' : ['r-l', [-2,0], 'right'],
8009                     'right' : ['l-r', [2,0], 'left'],
8010                     'bottom' : ['tl-bl', [0,2], 'top'],
8011                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8012                 }
8013             });
8014             
8015             this.toolTip.render(Roo.get(document.body));
8016
8017             this.toolTip.el.enableDisplayMode("block");
8018             
8019             Roo.get(document.body).on('click', function(){
8020                 this.unmask();
8021             }, this);
8022             
8023             this.isApplied = true
8024         },
8025         
8026         mask : function(form, target)
8027         {
8028             this.form = form;
8029             
8030             this.target = target;
8031             
8032             if(!this.form.errorMask || !target.el){
8033                 return;
8034             }
8035             
8036             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8037             
8038             var scrolled = scrollable.getScroll();
8039             
8040             var ot = this.target.el.calcOffsetsTo(scrollable);
8041             
8042             scrollTo = ot[1] - 100;
8043             
8044             scrollable.scrollTo('top', scrollTo);
8045             
8046             var box = this.target.el.getBox();
8047
8048             var zIndex = Roo.bootstrap.Modal.zIndex++;
8049
8050             this.maskEl.top.setStyle('position', 'fixed');
8051             this.maskEl.top.setStyle('z-index', zIndex);
8052             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8053             this.maskEl.top.setXY([0, 0]);
8054             this.maskEl.top.show();
8055
8056             this.maskEl.left.setStyle('position', 'fixed');
8057             this.maskEl.left.setStyle('z-index', zIndex);
8058             this.maskEl.left.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8059             this.maskEl.left.setXY([box.right + this.padding, box.y - this.padding]);
8060             this.maskEl.left.show();
8061
8062             this.maskEl.bottom.setStyle('position', 'fixed');
8063             this.maskEl.bottom.setStyle('z-index', zIndex);
8064             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8065             this.maskEl.bottom.setXY([0, box.bottom + this.padding]);
8066             this.maskEl.bottom.show();
8067
8068             this.maskEl.right.setStyle('position', 'fixed');
8069             this.maskEl.right.setStyle('z-index', zIndex);
8070             this.maskEl.right.setSize(box.x - this.padding, box.height + this.padding * 2);
8071             this.maskEl.right.setXY([0, box.y - this.padding]);
8072             this.maskEl.right.show();
8073
8074
8075             this.toolTip.bindEl = this.target.el;
8076
8077             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8078
8079             var tip = this.target.blankText;
8080
8081             if(this.target.getValue() !== '' && this.target.regexText.length){
8082                 tip = this.target.regexText;
8083             }
8084
8085             this.toolTip.show(tip);
8086
8087             this.intervalID = window.setInterval(function() {
8088                 Roo.bootstrap.Form.popover.unmask();
8089             }, 10000);
8090
8091             window.onwheel = function(){ return false;};
8092             
8093             (function(){ this.isMasked = true; }).defer(500, this);
8094                 
8095             
8096             
8097         },
8098         
8099         unmask : function()
8100         {
8101             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8102                 return;
8103             }
8104             
8105             this.maskEl.top.setStyle('position', 'absolute');
8106             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8107             this.maskEl.top.hide();
8108
8109             this.maskEl.left.setStyle('position', 'absolute');
8110             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8111             this.maskEl.left.hide();
8112
8113             this.maskEl.bottom.setStyle('position', 'absolute');
8114             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8115             this.maskEl.bottom.hide();
8116
8117             this.maskEl.right.setStyle('position', 'absolute');
8118             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8119             this.maskEl.right.hide();
8120             
8121             this.toolTip.hide();
8122             
8123             this.toolTip.el.hide();
8124             
8125             window.onwheel = function(){ return true;};
8126             
8127             if(this.intervalID){
8128                 window.clearInterval(this.intervalID);
8129                 this.intervalID = false;
8130             }
8131             
8132             this.isMasked = false;
8133             
8134         }
8135         
8136     }
8137     
8138 });
8139
8140 /*
8141  * Based on:
8142  * Ext JS Library 1.1.1
8143  * Copyright(c) 2006-2007, Ext JS, LLC.
8144  *
8145  * Originally Released Under LGPL - original licence link has changed is not relivant.
8146  *
8147  * Fork - LGPL
8148  * <script type="text/javascript">
8149  */
8150 /**
8151  * @class Roo.form.VTypes
8152  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8153  * @singleton
8154  */
8155 Roo.form.VTypes = function(){
8156     // closure these in so they are only created once.
8157     var alpha = /^[a-zA-Z_]+$/;
8158     var alphanum = /^[a-zA-Z0-9_]+$/;
8159     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8160     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8161
8162     // All these messages and functions are configurable
8163     return {
8164         /**
8165          * The function used to validate email addresses
8166          * @param {String} value The email address
8167          */
8168         'email' : function(v){
8169             return email.test(v);
8170         },
8171         /**
8172          * The error text to display when the email validation function returns false
8173          * @type String
8174          */
8175         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8176         /**
8177          * The keystroke filter mask to be applied on email input
8178          * @type RegExp
8179          */
8180         'emailMask' : /[a-z0-9_\.\-@]/i,
8181
8182         /**
8183          * The function used to validate URLs
8184          * @param {String} value The URL
8185          */
8186         'url' : function(v){
8187             return url.test(v);
8188         },
8189         /**
8190          * The error text to display when the url validation function returns false
8191          * @type String
8192          */
8193         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8194         
8195         /**
8196          * The function used to validate alpha values
8197          * @param {String} value The value
8198          */
8199         'alpha' : function(v){
8200             return alpha.test(v);
8201         },
8202         /**
8203          * The error text to display when the alpha validation function returns false
8204          * @type String
8205          */
8206         'alphaText' : 'This field should only contain letters and _',
8207         /**
8208          * The keystroke filter mask to be applied on alpha input
8209          * @type RegExp
8210          */
8211         'alphaMask' : /[a-z_]/i,
8212
8213         /**
8214          * The function used to validate alphanumeric values
8215          * @param {String} value The value
8216          */
8217         'alphanum' : function(v){
8218             return alphanum.test(v);
8219         },
8220         /**
8221          * The error text to display when the alphanumeric validation function returns false
8222          * @type String
8223          */
8224         'alphanumText' : 'This field should only contain letters, numbers and _',
8225         /**
8226          * The keystroke filter mask to be applied on alphanumeric input
8227          * @type RegExp
8228          */
8229         'alphanumMask' : /[a-z0-9_]/i
8230     };
8231 }();/*
8232  * - LGPL
8233  *
8234  * Input
8235  * 
8236  */
8237
8238 /**
8239  * @class Roo.bootstrap.Input
8240  * @extends Roo.bootstrap.Component
8241  * Bootstrap Input class
8242  * @cfg {Boolean} disabled is it disabled
8243  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8244  * @cfg {String} name name of the input
8245  * @cfg {string} fieldLabel - the label associated
8246  * @cfg {string} placeholder - placeholder to put in text.
8247  * @cfg {string}  before - input group add on before
8248  * @cfg {string} after - input group add on after
8249  * @cfg {string} size - (lg|sm) or leave empty..
8250  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8251  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8252  * @cfg {Number} md colspan out of 12 for computer-sized screens
8253  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8254  * @cfg {string} value default value of the input
8255  * @cfg {Number} labelWidth set the width of label 
8256  * @cfg {Number} labellg set the width of label (1-12)
8257  * @cfg {Number} labelmd set the width of label (1-12)
8258  * @cfg {Number} labelsm set the width of label (1-12)
8259  * @cfg {Number} labelxs set the width of label (1-12)
8260  * @cfg {String} labelAlign (top|left)
8261  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8262  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8263  * @cfg {String} indicatorpos (left|right) default left
8264
8265  * @cfg {String} align (left|center|right) Default left
8266  * @cfg {Boolean} forceFeedback (true|false) Default false
8267  * 
8268  * 
8269  * 
8270  * 
8271  * @constructor
8272  * Create a new Input
8273  * @param {Object} config The config object
8274  */
8275
8276 Roo.bootstrap.Input = function(config){
8277     
8278     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8279     
8280     this.addEvents({
8281         /**
8282          * @event focus
8283          * Fires when this field receives input focus.
8284          * @param {Roo.form.Field} this
8285          */
8286         focus : true,
8287         /**
8288          * @event blur
8289          * Fires when this field loses input focus.
8290          * @param {Roo.form.Field} this
8291          */
8292         blur : true,
8293         /**
8294          * @event specialkey
8295          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8296          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8297          * @param {Roo.form.Field} this
8298          * @param {Roo.EventObject} e The event object
8299          */
8300         specialkey : true,
8301         /**
8302          * @event change
8303          * Fires just before the field blurs if the field value has changed.
8304          * @param {Roo.form.Field} this
8305          * @param {Mixed} newValue The new value
8306          * @param {Mixed} oldValue The original value
8307          */
8308         change : true,
8309         /**
8310          * @event invalid
8311          * Fires after the field has been marked as invalid.
8312          * @param {Roo.form.Field} this
8313          * @param {String} msg The validation message
8314          */
8315         invalid : true,
8316         /**
8317          * @event valid
8318          * Fires after the field has been validated with no errors.
8319          * @param {Roo.form.Field} this
8320          */
8321         valid : true,
8322          /**
8323          * @event keyup
8324          * Fires after the key up
8325          * @param {Roo.form.Field} this
8326          * @param {Roo.EventObject}  e The event Object
8327          */
8328         keyup : true
8329     });
8330 };
8331
8332 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8333      /**
8334      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8335       automatic validation (defaults to "keyup").
8336      */
8337     validationEvent : "keyup",
8338      /**
8339      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8340      */
8341     validateOnBlur : true,
8342     /**
8343      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8344      */
8345     validationDelay : 250,
8346      /**
8347      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8348      */
8349     focusClass : "x-form-focus",  // not needed???
8350     
8351        
8352     /**
8353      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8354      */
8355     invalidClass : "has-warning",
8356     
8357     /**
8358      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8359      */
8360     validClass : "has-success",
8361     
8362     /**
8363      * @cfg {Boolean} hasFeedback (true|false) default true
8364      */
8365     hasFeedback : true,
8366     
8367     /**
8368      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8369      */
8370     invalidFeedbackClass : "glyphicon-warning-sign",
8371     
8372     /**
8373      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8374      */
8375     validFeedbackClass : "glyphicon-ok",
8376     
8377     /**
8378      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8379      */
8380     selectOnFocus : false,
8381     
8382      /**
8383      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8384      */
8385     maskRe : null,
8386        /**
8387      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8388      */
8389     vtype : null,
8390     
8391       /**
8392      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8393      */
8394     disableKeyFilter : false,
8395     
8396        /**
8397      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8398      */
8399     disabled : false,
8400      /**
8401      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8402      */
8403     allowBlank : true,
8404     /**
8405      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8406      */
8407     blankText : "Please complete this mandatory field",
8408     
8409      /**
8410      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8411      */
8412     minLength : 0,
8413     /**
8414      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8415      */
8416     maxLength : Number.MAX_VALUE,
8417     /**
8418      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8419      */
8420     minLengthText : "The minimum length for this field is {0}",
8421     /**
8422      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8423      */
8424     maxLengthText : "The maximum length for this field is {0}",
8425   
8426     
8427     /**
8428      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8429      * If available, this function will be called only after the basic validators all return true, and will be passed the
8430      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8431      */
8432     validator : null,
8433     /**
8434      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8435      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8436      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8437      */
8438     regex : null,
8439     /**
8440      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8441      */
8442     regexText : "",
8443     
8444     autocomplete: false,
8445     
8446     
8447     fieldLabel : '',
8448     inputType : 'text',
8449     
8450     name : false,
8451     placeholder: false,
8452     before : false,
8453     after : false,
8454     size : false,
8455     hasFocus : false,
8456     preventMark: false,
8457     isFormField : true,
8458     value : '',
8459     labelWidth : 2,
8460     labelAlign : false,
8461     readOnly : false,
8462     align : false,
8463     formatedValue : false,
8464     forceFeedback : false,
8465     
8466     indicatorpos : 'left',
8467     
8468     labellg : 0,
8469     labelmd : 0,
8470     labelsm : 0,
8471     labelxs : 0,
8472     
8473     parentLabelAlign : function()
8474     {
8475         var parent = this;
8476         while (parent.parent()) {
8477             parent = parent.parent();
8478             if (typeof(parent.labelAlign) !='undefined') {
8479                 return parent.labelAlign;
8480             }
8481         }
8482         return 'left';
8483         
8484     },
8485     
8486     getAutoCreate : function()
8487     {
8488         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8489         
8490         var id = Roo.id();
8491         
8492         var cfg = {};
8493         
8494         if(this.inputType != 'hidden'){
8495             cfg.cls = 'form-group' //input-group
8496         }
8497         
8498         var input =  {
8499             tag: 'input',
8500             id : id,
8501             type : this.inputType,
8502             value : this.value,
8503             cls : 'form-control',
8504             placeholder : this.placeholder || '',
8505             autocomplete : this.autocomplete || 'new-password'
8506         };
8507         
8508         if(this.align){
8509             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8510         }
8511         
8512         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8513             input.maxLength = this.maxLength;
8514         }
8515         
8516         if (this.disabled) {
8517             input.disabled=true;
8518         }
8519         
8520         if (this.readOnly) {
8521             input.readonly=true;
8522         }
8523         
8524         if (this.name) {
8525             input.name = this.name;
8526         }
8527         
8528         if (this.size) {
8529             input.cls += ' input-' + this.size;
8530         }
8531         
8532         var settings=this;
8533         ['xs','sm','md','lg'].map(function(size){
8534             if (settings[size]) {
8535                 cfg.cls += ' col-' + size + '-' + settings[size];
8536             }
8537         });
8538         
8539         var inputblock = input;
8540         
8541         var feedback = {
8542             tag: 'span',
8543             cls: 'glyphicon form-control-feedback'
8544         };
8545             
8546         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8547             
8548             inputblock = {
8549                 cls : 'has-feedback',
8550                 cn :  [
8551                     input,
8552                     feedback
8553                 ] 
8554             };  
8555         }
8556         
8557         if (this.before || this.after) {
8558             
8559             inputblock = {
8560                 cls : 'input-group',
8561                 cn :  [] 
8562             };
8563             
8564             if (this.before && typeof(this.before) == 'string') {
8565                 
8566                 inputblock.cn.push({
8567                     tag :'span',
8568                     cls : 'roo-input-before input-group-addon',
8569                     html : this.before
8570                 });
8571             }
8572             if (this.before && typeof(this.before) == 'object') {
8573                 this.before = Roo.factory(this.before);
8574                 
8575                 inputblock.cn.push({
8576                     tag :'span',
8577                     cls : 'roo-input-before input-group-' +
8578                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8579                 });
8580             }
8581             
8582             inputblock.cn.push(input);
8583             
8584             if (this.after && typeof(this.after) == 'string') {
8585                 inputblock.cn.push({
8586                     tag :'span',
8587                     cls : 'roo-input-after input-group-addon',
8588                     html : this.after
8589                 });
8590             }
8591             if (this.after && typeof(this.after) == 'object') {
8592                 this.after = Roo.factory(this.after);
8593                 
8594                 inputblock.cn.push({
8595                     tag :'span',
8596                     cls : 'roo-input-after input-group-' +
8597                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8598                 });
8599             }
8600             
8601             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8602                 inputblock.cls += ' has-feedback';
8603                 inputblock.cn.push(feedback);
8604             }
8605         };
8606         
8607         if (align ==='left' && this.fieldLabel.length) {
8608             
8609             cfg.cls += ' roo-form-group-label-left';
8610             
8611             cfg.cn = [
8612                 {
8613                     tag : 'i',
8614                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8615                     tooltip : 'This field is required'
8616                 },
8617                 {
8618                     tag: 'label',
8619                     'for' :  id,
8620                     cls : 'control-label',
8621                     html : this.fieldLabel
8622
8623                 },
8624                 {
8625                     cls : "", 
8626                     cn: [
8627                         inputblock
8628                     ]
8629                 }
8630             ];
8631             
8632             var labelCfg = cfg.cn[1];
8633             var contentCfg = cfg.cn[2];
8634             
8635             if(this.indicatorpos == 'right'){
8636                 cfg.cn = [
8637                     {
8638                         tag: 'label',
8639                         'for' :  id,
8640                         cls : 'control-label',
8641                         html : this.fieldLabel
8642
8643                     },
8644                     {
8645                         tag : 'i',
8646                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8647                         tooltip : 'This field is required'
8648                     },
8649                     {
8650                         cls : "",
8651                         cn: [
8652                             inputblock
8653                         ]
8654                     }
8655
8656                 ];
8657                 
8658                 labelCfg = cfg.cn[0];
8659                 contentCfg = cfg.cn[2];
8660             
8661             }
8662             
8663             if(this.labelWidth > 12){
8664                 labelCfg.style = "width: " + this.labelWidth + 'px';
8665             }
8666             
8667             if(this.labelWidth < 13 && this.labelmd == 0){
8668                 this.labelmd = this.labelWidth;
8669             }
8670             
8671             if(this.labellg > 0){
8672                 labelCfg.cls += ' col-lg-' + this.labellg;
8673                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8674             }
8675             
8676             if(this.labelmd > 0){
8677                 labelCfg.cls += ' col-md-' + this.labelmd;
8678                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8679             }
8680             
8681             if(this.labelsm > 0){
8682                 labelCfg.cls += ' col-sm-' + this.labelsm;
8683                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8684             }
8685             
8686             if(this.labelxs > 0){
8687                 labelCfg.cls += ' col-xs-' + this.labelxs;
8688                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8689             }
8690             
8691             
8692         } else if ( this.fieldLabel.length) {
8693                 
8694             cfg.cn = [
8695                 {
8696                     tag : 'i',
8697                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8698                     tooltip : 'This field is required'
8699                 },
8700                 {
8701                     tag: 'label',
8702                    //cls : 'input-group-addon',
8703                     html : this.fieldLabel
8704
8705                 },
8706
8707                inputblock
8708
8709            ];
8710            
8711            if(this.indicatorpos == 'right'){
8712                 
8713                 cfg.cn = [
8714                     {
8715                         tag: 'label',
8716                        //cls : 'input-group-addon',
8717                         html : this.fieldLabel
8718
8719                     },
8720                     {
8721                         tag : 'i',
8722                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8723                         tooltip : 'This field is required'
8724                     },
8725
8726                    inputblock
8727
8728                ];
8729
8730             }
8731
8732         } else {
8733             
8734             cfg.cn = [
8735
8736                     inputblock
8737
8738             ];
8739                 
8740                 
8741         };
8742         
8743         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8744            cfg.cls += ' navbar-form';
8745         }
8746         
8747         if (this.parentType === 'NavGroup') {
8748            cfg.cls += ' navbar-form';
8749            cfg.tag = 'li';
8750         }
8751         
8752         return cfg;
8753         
8754     },
8755     /**
8756      * return the real input element.
8757      */
8758     inputEl: function ()
8759     {
8760         return this.el.select('input.form-control',true).first();
8761     },
8762     
8763     tooltipEl : function()
8764     {
8765         return this.inputEl();
8766     },
8767     
8768     indicatorEl : function()
8769     {
8770         var indicator = this.el.select('i.roo-required-indicator',true).first();
8771         
8772         if(!indicator){
8773             return false;
8774         }
8775         
8776         return indicator;
8777         
8778     },
8779     
8780     setDisabled : function(v)
8781     {
8782         var i  = this.inputEl().dom;
8783         if (!v) {
8784             i.removeAttribute('disabled');
8785             return;
8786             
8787         }
8788         i.setAttribute('disabled','true');
8789     },
8790     initEvents : function()
8791     {
8792           
8793         this.inputEl().on("keydown" , this.fireKey,  this);
8794         this.inputEl().on("focus", this.onFocus,  this);
8795         this.inputEl().on("blur", this.onBlur,  this);
8796         
8797         this.inputEl().relayEvent('keyup', this);
8798         
8799         this.indicator = this.indicatorEl();
8800         
8801         if(this.indicator){
8802             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8803             this.indicator.hide();
8804         }
8805  
8806         // reference to original value for reset
8807         this.originalValue = this.getValue();
8808         //Roo.form.TextField.superclass.initEvents.call(this);
8809         if(this.validationEvent == 'keyup'){
8810             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8811             this.inputEl().on('keyup', this.filterValidation, this);
8812         }
8813         else if(this.validationEvent !== false){
8814             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8815         }
8816         
8817         if(this.selectOnFocus){
8818             this.on("focus", this.preFocus, this);
8819             
8820         }
8821         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8822             this.inputEl().on("keypress", this.filterKeys, this);
8823         } else {
8824             this.inputEl().relayEvent('keypress', this);
8825         }
8826        /* if(this.grow){
8827             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8828             this.el.on("click", this.autoSize,  this);
8829         }
8830         */
8831         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8832             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8833         }
8834         
8835         if (typeof(this.before) == 'object') {
8836             this.before.render(this.el.select('.roo-input-before',true).first());
8837         }
8838         if (typeof(this.after) == 'object') {
8839             this.after.render(this.el.select('.roo-input-after',true).first());
8840         }
8841         
8842         
8843     },
8844     filterValidation : function(e){
8845         if(!e.isNavKeyPress()){
8846             this.validationTask.delay(this.validationDelay);
8847         }
8848     },
8849      /**
8850      * Validates the field value
8851      * @return {Boolean} True if the value is valid, else false
8852      */
8853     validate : function(){
8854         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8855         if(this.disabled || this.validateValue(this.getRawValue())){
8856             this.markValid();
8857             return true;
8858         }
8859         
8860         this.markInvalid();
8861         return false;
8862     },
8863     
8864     
8865     /**
8866      * Validates a value according to the field's validation rules and marks the field as invalid
8867      * if the validation fails
8868      * @param {Mixed} value The value to validate
8869      * @return {Boolean} True if the value is valid, else false
8870      */
8871     validateValue : function(value){
8872         if(value.length < 1)  { // if it's blank
8873             if(this.allowBlank){
8874                 return true;
8875             }
8876             return false;
8877         }
8878         
8879         if(value.length < this.minLength){
8880             return false;
8881         }
8882         if(value.length > this.maxLength){
8883             return false;
8884         }
8885         if(this.vtype){
8886             var vt = Roo.form.VTypes;
8887             if(!vt[this.vtype](value, this)){
8888                 return false;
8889             }
8890         }
8891         if(typeof this.validator == "function"){
8892             var msg = this.validator(value);
8893             if(msg !== true){
8894                 return false;
8895             }
8896         }
8897         
8898         if(this.regex && !this.regex.test(value)){
8899             return false;
8900         }
8901         
8902         return true;
8903     },
8904
8905     
8906     
8907      // private
8908     fireKey : function(e){
8909         //Roo.log('field ' + e.getKey());
8910         if(e.isNavKeyPress()){
8911             this.fireEvent("specialkey", this, e);
8912         }
8913     },
8914     focus : function (selectText){
8915         if(this.rendered){
8916             this.inputEl().focus();
8917             if(selectText === true){
8918                 this.inputEl().dom.select();
8919             }
8920         }
8921         return this;
8922     } ,
8923     
8924     onFocus : function(){
8925         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8926            // this.el.addClass(this.focusClass);
8927         }
8928         if(!this.hasFocus){
8929             this.hasFocus = true;
8930             this.startValue = this.getValue();
8931             this.fireEvent("focus", this);
8932         }
8933     },
8934     
8935     beforeBlur : Roo.emptyFn,
8936
8937     
8938     // private
8939     onBlur : function(){
8940         this.beforeBlur();
8941         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8942             //this.el.removeClass(this.focusClass);
8943         }
8944         this.hasFocus = false;
8945         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8946             this.validate();
8947         }
8948         var v = this.getValue();
8949         if(String(v) !== String(this.startValue)){
8950             this.fireEvent('change', this, v, this.startValue);
8951         }
8952         this.fireEvent("blur", this);
8953     },
8954     
8955     /**
8956      * Resets the current field value to the originally loaded value and clears any validation messages
8957      */
8958     reset : function(){
8959         this.setValue(this.originalValue);
8960         this.validate();
8961     },
8962      /**
8963      * Returns the name of the field
8964      * @return {Mixed} name The name field
8965      */
8966     getName: function(){
8967         return this.name;
8968     },
8969      /**
8970      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8971      * @return {Mixed} value The field value
8972      */
8973     getValue : function(){
8974         
8975         var v = this.inputEl().getValue();
8976         
8977         return v;
8978     },
8979     /**
8980      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8981      * @return {Mixed} value The field value
8982      */
8983     getRawValue : function(){
8984         var v = this.inputEl().getValue();
8985         
8986         return v;
8987     },
8988     
8989     /**
8990      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8991      * @param {Mixed} value The value to set
8992      */
8993     setRawValue : function(v){
8994         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8995     },
8996     
8997     selectText : function(start, end){
8998         var v = this.getRawValue();
8999         if(v.length > 0){
9000             start = start === undefined ? 0 : start;
9001             end = end === undefined ? v.length : end;
9002             var d = this.inputEl().dom;
9003             if(d.setSelectionRange){
9004                 d.setSelectionRange(start, end);
9005             }else if(d.createTextRange){
9006                 var range = d.createTextRange();
9007                 range.moveStart("character", start);
9008                 range.moveEnd("character", v.length-end);
9009                 range.select();
9010             }
9011         }
9012     },
9013     
9014     /**
9015      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9016      * @param {Mixed} value The value to set
9017      */
9018     setValue : function(v){
9019         this.value = v;
9020         if(this.rendered){
9021             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9022             this.validate();
9023         }
9024     },
9025     
9026     /*
9027     processValue : function(value){
9028         if(this.stripCharsRe){
9029             var newValue = value.replace(this.stripCharsRe, '');
9030             if(newValue !== value){
9031                 this.setRawValue(newValue);
9032                 return newValue;
9033             }
9034         }
9035         return value;
9036     },
9037   */
9038     preFocus : function(){
9039         
9040         if(this.selectOnFocus){
9041             this.inputEl().dom.select();
9042         }
9043     },
9044     filterKeys : function(e){
9045         var k = e.getKey();
9046         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9047             return;
9048         }
9049         var c = e.getCharCode(), cc = String.fromCharCode(c);
9050         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9051             return;
9052         }
9053         if(!this.maskRe.test(cc)){
9054             e.stopEvent();
9055         }
9056     },
9057      /**
9058      * Clear any invalid styles/messages for this field
9059      */
9060     clearInvalid : function(){
9061         
9062         if(!this.el || this.preventMark){ // not rendered
9063             return;
9064         }
9065         
9066         if(this.indicator){
9067             this.indicator.hide();
9068         }
9069         
9070         this.el.removeClass(this.invalidClass);
9071         
9072         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9073             
9074             var feedback = this.el.select('.form-control-feedback', true).first();
9075             
9076             if(feedback){
9077                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9078             }
9079             
9080         }
9081         
9082         this.fireEvent('valid', this);
9083     },
9084     
9085      /**
9086      * Mark this field as valid
9087      */
9088     markValid : function()
9089     {
9090         if(!this.el  || this.preventMark){ // not rendered...
9091             return;
9092         }
9093         
9094         this.el.removeClass([this.invalidClass, this.validClass]);
9095         
9096         var feedback = this.el.select('.form-control-feedback', true).first();
9097             
9098         if(feedback){
9099             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9100         }
9101
9102         if(this.disabled){
9103             return;
9104         }
9105         
9106         if(this.allowBlank && !this.getRawValue().length){
9107             return;
9108         }
9109         
9110         if(this.indicator){
9111             this.indicator.hide();
9112         }
9113         
9114         this.el.addClass(this.validClass);
9115         
9116         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9117             
9118             var feedback = this.el.select('.form-control-feedback', true).first();
9119             
9120             if(feedback){
9121                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9122                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9123             }
9124             
9125         }
9126         
9127         this.fireEvent('valid', this);
9128     },
9129     
9130      /**
9131      * Mark this field as invalid
9132      * @param {String} msg The validation message
9133      */
9134     markInvalid : function(msg)
9135     {
9136         if(!this.el  || this.preventMark){ // not rendered
9137             return;
9138         }
9139         
9140         this.el.removeClass([this.invalidClass, this.validClass]);
9141         
9142         var feedback = this.el.select('.form-control-feedback', true).first();
9143             
9144         if(feedback){
9145             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9146         }
9147
9148         if(this.disabled){
9149             return;
9150         }
9151         
9152         if(this.allowBlank && !this.getRawValue().length){
9153             return;
9154         }
9155         
9156         if(this.indicator){
9157             this.indicator.show();
9158         }
9159         
9160         this.el.addClass(this.invalidClass);
9161         
9162         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9163             
9164             var feedback = this.el.select('.form-control-feedback', true).first();
9165             
9166             if(feedback){
9167                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9168                 
9169                 if(this.getValue().length || this.forceFeedback){
9170                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9171                 }
9172                 
9173             }
9174             
9175         }
9176         
9177         this.fireEvent('invalid', this, msg);
9178     },
9179     // private
9180     SafariOnKeyDown : function(event)
9181     {
9182         // this is a workaround for a password hang bug on chrome/ webkit.
9183         if (this.inputEl().dom.type != 'password') {
9184             return;
9185         }
9186         
9187         var isSelectAll = false;
9188         
9189         if(this.inputEl().dom.selectionEnd > 0){
9190             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9191         }
9192         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9193             event.preventDefault();
9194             this.setValue('');
9195             return;
9196         }
9197         
9198         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9199             
9200             event.preventDefault();
9201             // this is very hacky as keydown always get's upper case.
9202             //
9203             var cc = String.fromCharCode(event.getCharCode());
9204             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9205             
9206         }
9207     },
9208     adjustWidth : function(tag, w){
9209         tag = tag.toLowerCase();
9210         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9211             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9212                 if(tag == 'input'){
9213                     return w + 2;
9214                 }
9215                 if(tag == 'textarea'){
9216                     return w-2;
9217                 }
9218             }else if(Roo.isOpera){
9219                 if(tag == 'input'){
9220                     return w + 2;
9221                 }
9222                 if(tag == 'textarea'){
9223                     return w-2;
9224                 }
9225             }
9226         }
9227         return w;
9228     }
9229     
9230 });
9231
9232  
9233 /*
9234  * - LGPL
9235  *
9236  * Input
9237  * 
9238  */
9239
9240 /**
9241  * @class Roo.bootstrap.TextArea
9242  * @extends Roo.bootstrap.Input
9243  * Bootstrap TextArea class
9244  * @cfg {Number} cols Specifies the visible width of a text area
9245  * @cfg {Number} rows Specifies the visible number of lines in a text area
9246  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9247  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9248  * @cfg {string} html text
9249  * 
9250  * @constructor
9251  * Create a new TextArea
9252  * @param {Object} config The config object
9253  */
9254
9255 Roo.bootstrap.TextArea = function(config){
9256     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9257    
9258 };
9259
9260 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9261      
9262     cols : false,
9263     rows : 5,
9264     readOnly : false,
9265     warp : 'soft',
9266     resize : false,
9267     value: false,
9268     html: false,
9269     
9270     getAutoCreate : function(){
9271         
9272         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9273         
9274         var id = Roo.id();
9275         
9276         var cfg = {};
9277         
9278         var input =  {
9279             tag: 'textarea',
9280             id : id,
9281             warp : this.warp,
9282             rows : this.rows,
9283             value : this.value || '',
9284             html: this.html || '',
9285             cls : 'form-control',
9286             placeholder : this.placeholder || '' 
9287             
9288         };
9289         
9290         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9291             input.maxLength = this.maxLength;
9292         }
9293         
9294         if(this.resize){
9295             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9296         }
9297         
9298         if(this.cols){
9299             input.cols = this.cols;
9300         }
9301         
9302         if (this.readOnly) {
9303             input.readonly = true;
9304         }
9305         
9306         if (this.name) {
9307             input.name = this.name;
9308         }
9309         
9310         if (this.size) {
9311             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9312         }
9313         
9314         var settings=this;
9315         ['xs','sm','md','lg'].map(function(size){
9316             if (settings[size]) {
9317                 cfg.cls += ' col-' + size + '-' + settings[size];
9318             }
9319         });
9320         
9321         var inputblock = input;
9322         
9323         if(this.hasFeedback && !this.allowBlank){
9324             
9325             var feedback = {
9326                 tag: 'span',
9327                 cls: 'glyphicon form-control-feedback'
9328             };
9329
9330             inputblock = {
9331                 cls : 'has-feedback',
9332                 cn :  [
9333                     input,
9334                     feedback
9335                 ] 
9336             };  
9337         }
9338         
9339         
9340         if (this.before || this.after) {
9341             
9342             inputblock = {
9343                 cls : 'input-group',
9344                 cn :  [] 
9345             };
9346             if (this.before) {
9347                 inputblock.cn.push({
9348                     tag :'span',
9349                     cls : 'input-group-addon',
9350                     html : this.before
9351                 });
9352             }
9353             
9354             inputblock.cn.push(input);
9355             
9356             if(this.hasFeedback && !this.allowBlank){
9357                 inputblock.cls += ' has-feedback';
9358                 inputblock.cn.push(feedback);
9359             }
9360             
9361             if (this.after) {
9362                 inputblock.cn.push({
9363                     tag :'span',
9364                     cls : 'input-group-addon',
9365                     html : this.after
9366                 });
9367             }
9368             
9369         }
9370         
9371         if (align ==='left' && this.fieldLabel.length) {
9372             cfg.cn = [
9373                 {
9374                     tag: 'label',
9375                     'for' :  id,
9376                     cls : 'control-label',
9377                     html : this.fieldLabel
9378                 },
9379                 {
9380                     cls : "",
9381                     cn: [
9382                         inputblock
9383                     ]
9384                 }
9385
9386             ];
9387             
9388             if(this.labelWidth > 12){
9389                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9390             }
9391
9392             if(this.labelWidth < 13 && this.labelmd == 0){
9393                 this.labelmd = this.labelWidth;
9394             }
9395
9396             if(this.labellg > 0){
9397                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9398                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9399             }
9400
9401             if(this.labelmd > 0){
9402                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9403                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9404             }
9405
9406             if(this.labelsm > 0){
9407                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9408                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9409             }
9410
9411             if(this.labelxs > 0){
9412                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9413                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9414             }
9415             
9416         } else if ( this.fieldLabel.length) {
9417             cfg.cn = [
9418
9419                {
9420                    tag: 'label',
9421                    //cls : 'input-group-addon',
9422                    html : this.fieldLabel
9423
9424                },
9425
9426                inputblock
9427
9428            ];
9429
9430         } else {
9431
9432             cfg.cn = [
9433
9434                 inputblock
9435
9436             ];
9437                 
9438         }
9439         
9440         if (this.disabled) {
9441             input.disabled=true;
9442         }
9443         
9444         return cfg;
9445         
9446     },
9447     /**
9448      * return the real textarea element.
9449      */
9450     inputEl: function ()
9451     {
9452         return this.el.select('textarea.form-control',true).first();
9453     },
9454     
9455     /**
9456      * Clear any invalid styles/messages for this field
9457      */
9458     clearInvalid : function()
9459     {
9460         
9461         if(!this.el || this.preventMark){ // not rendered
9462             return;
9463         }
9464         
9465         var label = this.el.select('label', true).first();
9466         var icon = this.el.select('i.fa-star', true).first();
9467         
9468         if(label && icon){
9469             icon.remove();
9470         }
9471         
9472         this.el.removeClass(this.invalidClass);
9473         
9474         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9475             
9476             var feedback = this.el.select('.form-control-feedback', true).first();
9477             
9478             if(feedback){
9479                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9480             }
9481             
9482         }
9483         
9484         this.fireEvent('valid', this);
9485     },
9486     
9487      /**
9488      * Mark this field as valid
9489      */
9490     markValid : function()
9491     {
9492         if(!this.el  || this.preventMark){ // not rendered
9493             return;
9494         }
9495         
9496         this.el.removeClass([this.invalidClass, this.validClass]);
9497         
9498         var feedback = this.el.select('.form-control-feedback', true).first();
9499             
9500         if(feedback){
9501             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9502         }
9503
9504         if(this.disabled || this.allowBlank){
9505             return;
9506         }
9507         
9508         var label = this.el.select('label', true).first();
9509         var icon = this.el.select('i.fa-star', true).first();
9510         
9511         if(label && icon){
9512             icon.remove();
9513         }
9514         
9515         this.el.addClass(this.validClass);
9516         
9517         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9518             
9519             var feedback = this.el.select('.form-control-feedback', true).first();
9520             
9521             if(feedback){
9522                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9523                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9524             }
9525             
9526         }
9527         
9528         this.fireEvent('valid', this);
9529     },
9530     
9531      /**
9532      * Mark this field as invalid
9533      * @param {String} msg The validation message
9534      */
9535     markInvalid : function(msg)
9536     {
9537         if(!this.el  || this.preventMark){ // not rendered
9538             return;
9539         }
9540         
9541         this.el.removeClass([this.invalidClass, this.validClass]);
9542         
9543         var feedback = this.el.select('.form-control-feedback', true).first();
9544             
9545         if(feedback){
9546             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9547         }
9548
9549         if(this.disabled || this.allowBlank){
9550             return;
9551         }
9552         
9553         var label = this.el.select('label', true).first();
9554         var icon = this.el.select('i.fa-star', true).first();
9555         
9556         if(!this.getValue().length && label && !icon){
9557             this.el.createChild({
9558                 tag : 'i',
9559                 cls : 'text-danger fa fa-lg fa-star',
9560                 tooltip : 'This field is required',
9561                 style : 'margin-right:5px;'
9562             }, label, true);
9563         }
9564
9565         this.el.addClass(this.invalidClass);
9566         
9567         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9568             
9569             var feedback = this.el.select('.form-control-feedback', true).first();
9570             
9571             if(feedback){
9572                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9573                 
9574                 if(this.getValue().length || this.forceFeedback){
9575                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9576                 }
9577                 
9578             }
9579             
9580         }
9581         
9582         this.fireEvent('invalid', this, msg);
9583     }
9584 });
9585
9586  
9587 /*
9588  * - LGPL
9589  *
9590  * trigger field - base class for combo..
9591  * 
9592  */
9593  
9594 /**
9595  * @class Roo.bootstrap.TriggerField
9596  * @extends Roo.bootstrap.Input
9597  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9598  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9599  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9600  * for which you can provide a custom implementation.  For example:
9601  * <pre><code>
9602 var trigger = new Roo.bootstrap.TriggerField();
9603 trigger.onTriggerClick = myTriggerFn;
9604 trigger.applyTo('my-field');
9605 </code></pre>
9606  *
9607  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9608  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9609  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9610  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9611  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9612
9613  * @constructor
9614  * Create a new TriggerField.
9615  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9616  * to the base TextField)
9617  */
9618 Roo.bootstrap.TriggerField = function(config){
9619     this.mimicing = false;
9620     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9621 };
9622
9623 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9624     /**
9625      * @cfg {String} triggerClass A CSS class to apply to the trigger
9626      */
9627      /**
9628      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9629      */
9630     hideTrigger:false,
9631
9632     /**
9633      * @cfg {Boolean} removable (true|false) special filter default false
9634      */
9635     removable : false,
9636     
9637     /** @cfg {Boolean} grow @hide */
9638     /** @cfg {Number} growMin @hide */
9639     /** @cfg {Number} growMax @hide */
9640
9641     /**
9642      * @hide 
9643      * @method
9644      */
9645     autoSize: Roo.emptyFn,
9646     // private
9647     monitorTab : true,
9648     // private
9649     deferHeight : true,
9650
9651     
9652     actionMode : 'wrap',
9653     
9654     caret : false,
9655     
9656     
9657     getAutoCreate : function(){
9658        
9659         var align = this.labelAlign || this.parentLabelAlign();
9660         
9661         var id = Roo.id();
9662         
9663         var cfg = {
9664             cls: 'form-group' //input-group
9665         };
9666         
9667         
9668         var input =  {
9669             tag: 'input',
9670             id : id,
9671             type : this.inputType,
9672             cls : 'form-control',
9673             autocomplete: 'new-password',
9674             placeholder : this.placeholder || '' 
9675             
9676         };
9677         if (this.name) {
9678             input.name = this.name;
9679         }
9680         if (this.size) {
9681             input.cls += ' input-' + this.size;
9682         }
9683         
9684         if (this.disabled) {
9685             input.disabled=true;
9686         }
9687         
9688         var inputblock = input;
9689         
9690         if(this.hasFeedback && !this.allowBlank){
9691             
9692             var feedback = {
9693                 tag: 'span',
9694                 cls: 'glyphicon form-control-feedback'
9695             };
9696             
9697             if(this.removable && !this.editable && !this.tickable){
9698                 inputblock = {
9699                     cls : 'has-feedback',
9700                     cn :  [
9701                         inputblock,
9702                         {
9703                             tag: 'button',
9704                             html : 'x',
9705                             cls : 'roo-combo-removable-btn close'
9706                         },
9707                         feedback
9708                     ] 
9709                 };
9710             } else {
9711                 inputblock = {
9712                     cls : 'has-feedback',
9713                     cn :  [
9714                         inputblock,
9715                         feedback
9716                     ] 
9717                 };
9718             }
9719
9720         } else {
9721             if(this.removable && !this.editable && !this.tickable){
9722                 inputblock = {
9723                     cls : 'roo-removable',
9724                     cn :  [
9725                         inputblock,
9726                         {
9727                             tag: 'button',
9728                             html : 'x',
9729                             cls : 'roo-combo-removable-btn close'
9730                         }
9731                     ] 
9732                 };
9733             }
9734         }
9735         
9736         if (this.before || this.after) {
9737             
9738             inputblock = {
9739                 cls : 'input-group',
9740                 cn :  [] 
9741             };
9742             if (this.before) {
9743                 inputblock.cn.push({
9744                     tag :'span',
9745                     cls : 'input-group-addon',
9746                     html : this.before
9747                 });
9748             }
9749             
9750             inputblock.cn.push(input);
9751             
9752             if(this.hasFeedback && !this.allowBlank){
9753                 inputblock.cls += ' has-feedback';
9754                 inputblock.cn.push(feedback);
9755             }
9756             
9757             if (this.after) {
9758                 inputblock.cn.push({
9759                     tag :'span',
9760                     cls : 'input-group-addon',
9761                     html : this.after
9762                 });
9763             }
9764             
9765         };
9766         
9767         var box = {
9768             tag: 'div',
9769             cn: [
9770                 {
9771                     tag: 'input',
9772                     type : 'hidden',
9773                     cls: 'form-hidden-field'
9774                 },
9775                 inputblock
9776             ]
9777             
9778         };
9779         
9780         if(this.multiple){
9781             box = {
9782                 tag: 'div',
9783                 cn: [
9784                     {
9785                         tag: 'input',
9786                         type : 'hidden',
9787                         cls: 'form-hidden-field'
9788                     },
9789                     {
9790                         tag: 'ul',
9791                         cls: 'roo-select2-choices',
9792                         cn:[
9793                             {
9794                                 tag: 'li',
9795                                 cls: 'roo-select2-search-field',
9796                                 cn: [
9797
9798                                     inputblock
9799                                 ]
9800                             }
9801                         ]
9802                     }
9803                 ]
9804             }
9805         };
9806         
9807         var combobox = {
9808             cls: 'roo-select2-container input-group',
9809             cn: [
9810                 box
9811 //                {
9812 //                    tag: 'ul',
9813 //                    cls: 'typeahead typeahead-long dropdown-menu',
9814 //                    style: 'display:none'
9815 //                }
9816             ]
9817         };
9818         
9819         if(!this.multiple && this.showToggleBtn){
9820             
9821             var caret = {
9822                         tag: 'span',
9823                         cls: 'caret'
9824              };
9825             if (this.caret != false) {
9826                 caret = {
9827                      tag: 'i',
9828                      cls: 'fa fa-' + this.caret
9829                 };
9830                 
9831             }
9832             
9833             combobox.cn.push({
9834                 tag :'span',
9835                 cls : 'input-group-addon btn dropdown-toggle',
9836                 cn : [
9837                     caret,
9838                     {
9839                         tag: 'span',
9840                         cls: 'combobox-clear',
9841                         cn  : [
9842                             {
9843                                 tag : 'i',
9844                                 cls: 'icon-remove'
9845                             }
9846                         ]
9847                     }
9848                 ]
9849
9850             })
9851         }
9852         
9853         if(this.multiple){
9854             combobox.cls += ' roo-select2-container-multi';
9855         }
9856         
9857         if (align ==='left' && this.fieldLabel.length) {
9858             
9859             cfg.cls += ' roo-form-group-label-left';
9860
9861             cfg.cn = [
9862                 {
9863                     tag : 'i',
9864                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9865                     tooltip : 'This field is required'
9866                 },
9867                 {
9868                     tag: 'label',
9869                     'for' :  id,
9870                     cls : 'control-label',
9871                     html : this.fieldLabel
9872
9873                 },
9874                 {
9875                     cls : "", 
9876                     cn: [
9877                         combobox
9878                     ]
9879                 }
9880
9881             ];
9882             
9883             var labelCfg = cfg.cn[1];
9884             var contentCfg = cfg.cn[2];
9885             
9886             if(this.indicatorpos == 'right'){
9887                 cfg.cn = [
9888                     {
9889                         tag: 'label',
9890                         'for' :  id,
9891                         cls : 'control-label',
9892                         html : this.fieldLabel
9893                     },
9894                     {
9895                         tag : 'i',
9896                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9897                         tooltip : 'This field is required'
9898                     },
9899                     {
9900                         cls : "", 
9901                         cn: [
9902                             combobox
9903                         ]
9904                     }
9905
9906                 ];
9907                 
9908                 labelCfg = cfg.cn[0];
9909                 contentCfg = cfg.cn[2];
9910             }
9911             
9912             if(this.labelWidth > 12){
9913                 labelCfg.style = "width: " + this.labelWidth + 'px';
9914             }
9915             
9916             if(this.labelWidth < 13 && this.labelmd == 0){
9917                 this.labelmd = this.labelWidth;
9918             }
9919             
9920             if(this.labellg > 0){
9921                 labelCfg.cls += ' col-lg-' + this.labellg;
9922                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9923             }
9924             
9925             if(this.labelmd > 0){
9926                 labelCfg.cls += ' col-md-' + this.labelmd;
9927                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9928             }
9929             
9930             if(this.labelsm > 0){
9931                 labelCfg.cls += ' col-sm-' + this.labelsm;
9932                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9933             }
9934             
9935             if(this.labelxs > 0){
9936                 labelCfg.cls += ' col-xs-' + this.labelxs;
9937                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9938             }
9939             
9940         } else if ( this.fieldLabel.length) {
9941 //                Roo.log(" label");
9942             cfg.cn = [
9943                 {
9944                    tag : 'i',
9945                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9946                    tooltip : 'This field is required'
9947                },
9948                {
9949                    tag: 'label',
9950                    //cls : 'input-group-addon',
9951                    html : this.fieldLabel
9952
9953                },
9954
9955                combobox
9956
9957             ];
9958             
9959             if(this.indicatorpos == 'right'){
9960                 
9961                 cfg.cn = [
9962                     {
9963                        tag: 'label',
9964                        //cls : 'input-group-addon',
9965                        html : this.fieldLabel
9966
9967                     },
9968                     {
9969                        tag : 'i',
9970                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9971                        tooltip : 'This field is required'
9972                     },
9973                     
9974                     combobox
9975
9976                 ];
9977
9978             }
9979
9980         } else {
9981             
9982 //                Roo.log(" no label && no align");
9983                 cfg = combobox
9984                      
9985                 
9986         }
9987         
9988         var settings=this;
9989         ['xs','sm','md','lg'].map(function(size){
9990             if (settings[size]) {
9991                 cfg.cls += ' col-' + size + '-' + settings[size];
9992             }
9993         });
9994         
9995         return cfg;
9996         
9997     },
9998     
9999     
10000     
10001     // private
10002     onResize : function(w, h){
10003 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10004 //        if(typeof w == 'number'){
10005 //            var x = w - this.trigger.getWidth();
10006 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10007 //            this.trigger.setStyle('left', x+'px');
10008 //        }
10009     },
10010
10011     // private
10012     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10013
10014     // private
10015     getResizeEl : function(){
10016         return this.inputEl();
10017     },
10018
10019     // private
10020     getPositionEl : function(){
10021         return this.inputEl();
10022     },
10023
10024     // private
10025     alignErrorIcon : function(){
10026         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10027     },
10028
10029     // private
10030     initEvents : function(){
10031         
10032         this.createList();
10033         
10034         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10035         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10036         if(!this.multiple && this.showToggleBtn){
10037             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10038             if(this.hideTrigger){
10039                 this.trigger.setDisplayed(false);
10040             }
10041             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10042         }
10043         
10044         if(this.multiple){
10045             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10046         }
10047         
10048         if(this.removable && !this.editable && !this.tickable){
10049             var close = this.closeTriggerEl();
10050             
10051             if(close){
10052                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10053                 close.on('click', this.removeBtnClick, this, close);
10054             }
10055         }
10056         
10057         //this.trigger.addClassOnOver('x-form-trigger-over');
10058         //this.trigger.addClassOnClick('x-form-trigger-click');
10059         
10060         //if(!this.width){
10061         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10062         //}
10063     },
10064     
10065     closeTriggerEl : function()
10066     {
10067         var close = this.el.select('.roo-combo-removable-btn', true).first();
10068         return close ? close : false;
10069     },
10070     
10071     removeBtnClick : function(e, h, el)
10072     {
10073         e.preventDefault();
10074         
10075         if(this.fireEvent("remove", this) !== false){
10076             this.reset();
10077             this.fireEvent("afterremove", this)
10078         }
10079     },
10080     
10081     createList : function()
10082     {
10083         this.list = Roo.get(document.body).createChild({
10084             tag: 'ul',
10085             cls: 'typeahead typeahead-long dropdown-menu',
10086             style: 'display:none'
10087         });
10088         
10089         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10090         
10091     },
10092
10093     // private
10094     initTrigger : function(){
10095        
10096     },
10097
10098     // private
10099     onDestroy : function(){
10100         if(this.trigger){
10101             this.trigger.removeAllListeners();
10102           //  this.trigger.remove();
10103         }
10104         //if(this.wrap){
10105         //    this.wrap.remove();
10106         //}
10107         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10108     },
10109
10110     // private
10111     onFocus : function(){
10112         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10113         /*
10114         if(!this.mimicing){
10115             this.wrap.addClass('x-trigger-wrap-focus');
10116             this.mimicing = true;
10117             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10118             if(this.monitorTab){
10119                 this.el.on("keydown", this.checkTab, this);
10120             }
10121         }
10122         */
10123     },
10124
10125     // private
10126     checkTab : function(e){
10127         if(e.getKey() == e.TAB){
10128             this.triggerBlur();
10129         }
10130     },
10131
10132     // private
10133     onBlur : function(){
10134         // do nothing
10135     },
10136
10137     // private
10138     mimicBlur : function(e, t){
10139         /*
10140         if(!this.wrap.contains(t) && this.validateBlur()){
10141             this.triggerBlur();
10142         }
10143         */
10144     },
10145
10146     // private
10147     triggerBlur : function(){
10148         this.mimicing = false;
10149         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10150         if(this.monitorTab){
10151             this.el.un("keydown", this.checkTab, this);
10152         }
10153         //this.wrap.removeClass('x-trigger-wrap-focus');
10154         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10155     },
10156
10157     // private
10158     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10159     validateBlur : function(e, t){
10160         return true;
10161     },
10162
10163     // private
10164     onDisable : function(){
10165         this.inputEl().dom.disabled = true;
10166         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10167         //if(this.wrap){
10168         //    this.wrap.addClass('x-item-disabled');
10169         //}
10170     },
10171
10172     // private
10173     onEnable : function(){
10174         this.inputEl().dom.disabled = false;
10175         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10176         //if(this.wrap){
10177         //    this.el.removeClass('x-item-disabled');
10178         //}
10179     },
10180
10181     // private
10182     onShow : function(){
10183         var ae = this.getActionEl();
10184         
10185         if(ae){
10186             ae.dom.style.display = '';
10187             ae.dom.style.visibility = 'visible';
10188         }
10189     },
10190
10191     // private
10192     
10193     onHide : function(){
10194         var ae = this.getActionEl();
10195         ae.dom.style.display = 'none';
10196     },
10197
10198     /**
10199      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10200      * by an implementing function.
10201      * @method
10202      * @param {EventObject} e
10203      */
10204     onTriggerClick : Roo.emptyFn
10205 });
10206  /*
10207  * Based on:
10208  * Ext JS Library 1.1.1
10209  * Copyright(c) 2006-2007, Ext JS, LLC.
10210  *
10211  * Originally Released Under LGPL - original licence link has changed is not relivant.
10212  *
10213  * Fork - LGPL
10214  * <script type="text/javascript">
10215  */
10216
10217
10218 /**
10219  * @class Roo.data.SortTypes
10220  * @singleton
10221  * Defines the default sorting (casting?) comparison functions used when sorting data.
10222  */
10223 Roo.data.SortTypes = {
10224     /**
10225      * Default sort that does nothing
10226      * @param {Mixed} s The value being converted
10227      * @return {Mixed} The comparison value
10228      */
10229     none : function(s){
10230         return s;
10231     },
10232     
10233     /**
10234      * The regular expression used to strip tags
10235      * @type {RegExp}
10236      * @property
10237      */
10238     stripTagsRE : /<\/?[^>]+>/gi,
10239     
10240     /**
10241      * Strips all HTML tags to sort on text only
10242      * @param {Mixed} s The value being converted
10243      * @return {String} The comparison value
10244      */
10245     asText : function(s){
10246         return String(s).replace(this.stripTagsRE, "");
10247     },
10248     
10249     /**
10250      * Strips all HTML tags to sort on text only - Case insensitive
10251      * @param {Mixed} s The value being converted
10252      * @return {String} The comparison value
10253      */
10254     asUCText : function(s){
10255         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10256     },
10257     
10258     /**
10259      * Case insensitive string
10260      * @param {Mixed} s The value being converted
10261      * @return {String} The comparison value
10262      */
10263     asUCString : function(s) {
10264         return String(s).toUpperCase();
10265     },
10266     
10267     /**
10268      * Date sorting
10269      * @param {Mixed} s The value being converted
10270      * @return {Number} The comparison value
10271      */
10272     asDate : function(s) {
10273         if(!s){
10274             return 0;
10275         }
10276         if(s instanceof Date){
10277             return s.getTime();
10278         }
10279         return Date.parse(String(s));
10280     },
10281     
10282     /**
10283      * Float sorting
10284      * @param {Mixed} s The value being converted
10285      * @return {Float} The comparison value
10286      */
10287     asFloat : function(s) {
10288         var val = parseFloat(String(s).replace(/,/g, ""));
10289         if(isNaN(val)) {
10290             val = 0;
10291         }
10292         return val;
10293     },
10294     
10295     /**
10296      * Integer sorting
10297      * @param {Mixed} s The value being converted
10298      * @return {Number} The comparison value
10299      */
10300     asInt : function(s) {
10301         var val = parseInt(String(s).replace(/,/g, ""));
10302         if(isNaN(val)) {
10303             val = 0;
10304         }
10305         return val;
10306     }
10307 };/*
10308  * Based on:
10309  * Ext JS Library 1.1.1
10310  * Copyright(c) 2006-2007, Ext JS, LLC.
10311  *
10312  * Originally Released Under LGPL - original licence link has changed is not relivant.
10313  *
10314  * Fork - LGPL
10315  * <script type="text/javascript">
10316  */
10317
10318 /**
10319 * @class Roo.data.Record
10320  * Instances of this class encapsulate both record <em>definition</em> information, and record
10321  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10322  * to access Records cached in an {@link Roo.data.Store} object.<br>
10323  * <p>
10324  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10325  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10326  * objects.<br>
10327  * <p>
10328  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10329  * @constructor
10330  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10331  * {@link #create}. The parameters are the same.
10332  * @param {Array} data An associative Array of data values keyed by the field name.
10333  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10334  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10335  * not specified an integer id is generated.
10336  */
10337 Roo.data.Record = function(data, id){
10338     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10339     this.data = data;
10340 };
10341
10342 /**
10343  * Generate a constructor for a specific record layout.
10344  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10345  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10346  * Each field definition object may contain the following properties: <ul>
10347  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
10348  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10349  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10350  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10351  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10352  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10353  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10354  * this may be omitted.</p></li>
10355  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10356  * <ul><li>auto (Default, implies no conversion)</li>
10357  * <li>string</li>
10358  * <li>int</li>
10359  * <li>float</li>
10360  * <li>boolean</li>
10361  * <li>date</li></ul></p></li>
10362  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10363  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10364  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10365  * by the Reader into an object that will be stored in the Record. It is passed the
10366  * following parameters:<ul>
10367  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10368  * </ul></p></li>
10369  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10370  * </ul>
10371  * <br>usage:<br><pre><code>
10372 var TopicRecord = Roo.data.Record.create(
10373     {name: 'title', mapping: 'topic_title'},
10374     {name: 'author', mapping: 'username'},
10375     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10376     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10377     {name: 'lastPoster', mapping: 'user2'},
10378     {name: 'excerpt', mapping: 'post_text'}
10379 );
10380
10381 var myNewRecord = new TopicRecord({
10382     title: 'Do my job please',
10383     author: 'noobie',
10384     totalPosts: 1,
10385     lastPost: new Date(),
10386     lastPoster: 'Animal',
10387     excerpt: 'No way dude!'
10388 });
10389 myStore.add(myNewRecord);
10390 </code></pre>
10391  * @method create
10392  * @static
10393  */
10394 Roo.data.Record.create = function(o){
10395     var f = function(){
10396         f.superclass.constructor.apply(this, arguments);
10397     };
10398     Roo.extend(f, Roo.data.Record);
10399     var p = f.prototype;
10400     p.fields = new Roo.util.MixedCollection(false, function(field){
10401         return field.name;
10402     });
10403     for(var i = 0, len = o.length; i < len; i++){
10404         p.fields.add(new Roo.data.Field(o[i]));
10405     }
10406     f.getField = function(name){
10407         return p.fields.get(name);  
10408     };
10409     return f;
10410 };
10411
10412 Roo.data.Record.AUTO_ID = 1000;
10413 Roo.data.Record.EDIT = 'edit';
10414 Roo.data.Record.REJECT = 'reject';
10415 Roo.data.Record.COMMIT = 'commit';
10416
10417 Roo.data.Record.prototype = {
10418     /**
10419      * Readonly flag - true if this record has been modified.
10420      * @type Boolean
10421      */
10422     dirty : false,
10423     editing : false,
10424     error: null,
10425     modified: null,
10426
10427     // private
10428     join : function(store){
10429         this.store = store;
10430     },
10431
10432     /**
10433      * Set the named field to the specified value.
10434      * @param {String} name The name of the field to set.
10435      * @param {Object} value The value to set the field to.
10436      */
10437     set : function(name, value){
10438         if(this.data[name] == value){
10439             return;
10440         }
10441         this.dirty = true;
10442         if(!this.modified){
10443             this.modified = {};
10444         }
10445         if(typeof this.modified[name] == 'undefined'){
10446             this.modified[name] = this.data[name];
10447         }
10448         this.data[name] = value;
10449         if(!this.editing && this.store){
10450             this.store.afterEdit(this);
10451         }       
10452     },
10453
10454     /**
10455      * Get the value of the named field.
10456      * @param {String} name The name of the field to get the value of.
10457      * @return {Object} The value of the field.
10458      */
10459     get : function(name){
10460         return this.data[name]; 
10461     },
10462
10463     // private
10464     beginEdit : function(){
10465         this.editing = true;
10466         this.modified = {}; 
10467     },
10468
10469     // private
10470     cancelEdit : function(){
10471         this.editing = false;
10472         delete this.modified;
10473     },
10474
10475     // private
10476     endEdit : function(){
10477         this.editing = false;
10478         if(this.dirty && this.store){
10479             this.store.afterEdit(this);
10480         }
10481     },
10482
10483     /**
10484      * Usually called by the {@link Roo.data.Store} which owns the Record.
10485      * Rejects all changes made to the Record since either creation, or the last commit operation.
10486      * Modified fields are reverted to their original values.
10487      * <p>
10488      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10489      * of reject operations.
10490      */
10491     reject : function(){
10492         var m = this.modified;
10493         for(var n in m){
10494             if(typeof m[n] != "function"){
10495                 this.data[n] = m[n];
10496             }
10497         }
10498         this.dirty = false;
10499         delete this.modified;
10500         this.editing = false;
10501         if(this.store){
10502             this.store.afterReject(this);
10503         }
10504     },
10505
10506     /**
10507      * Usually called by the {@link Roo.data.Store} which owns the Record.
10508      * Commits all changes made to the Record since either creation, or the last commit operation.
10509      * <p>
10510      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10511      * of commit operations.
10512      */
10513     commit : function(){
10514         this.dirty = false;
10515         delete this.modified;
10516         this.editing = false;
10517         if(this.store){
10518             this.store.afterCommit(this);
10519         }
10520     },
10521
10522     // private
10523     hasError : function(){
10524         return this.error != null;
10525     },
10526
10527     // private
10528     clearError : function(){
10529         this.error = null;
10530     },
10531
10532     /**
10533      * Creates a copy of this record.
10534      * @param {String} id (optional) A new record id if you don't want to use this record's id
10535      * @return {Record}
10536      */
10537     copy : function(newId) {
10538         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10539     }
10540 };/*
10541  * Based on:
10542  * Ext JS Library 1.1.1
10543  * Copyright(c) 2006-2007, Ext JS, LLC.
10544  *
10545  * Originally Released Under LGPL - original licence link has changed is not relivant.
10546  *
10547  * Fork - LGPL
10548  * <script type="text/javascript">
10549  */
10550
10551
10552
10553 /**
10554  * @class Roo.data.Store
10555  * @extends Roo.util.Observable
10556  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10557  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10558  * <p>
10559  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
10560  * has no knowledge of the format of the data returned by the Proxy.<br>
10561  * <p>
10562  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10563  * instances from the data object. These records are cached and made available through accessor functions.
10564  * @constructor
10565  * Creates a new Store.
10566  * @param {Object} config A config object containing the objects needed for the Store to access data,
10567  * and read the data into Records.
10568  */
10569 Roo.data.Store = function(config){
10570     this.data = new Roo.util.MixedCollection(false);
10571     this.data.getKey = function(o){
10572         return o.id;
10573     };
10574     this.baseParams = {};
10575     // private
10576     this.paramNames = {
10577         "start" : "start",
10578         "limit" : "limit",
10579         "sort" : "sort",
10580         "dir" : "dir",
10581         "multisort" : "_multisort"
10582     };
10583
10584     if(config && config.data){
10585         this.inlineData = config.data;
10586         delete config.data;
10587     }
10588
10589     Roo.apply(this, config);
10590     
10591     if(this.reader){ // reader passed
10592         this.reader = Roo.factory(this.reader, Roo.data);
10593         this.reader.xmodule = this.xmodule || false;
10594         if(!this.recordType){
10595             this.recordType = this.reader.recordType;
10596         }
10597         if(this.reader.onMetaChange){
10598             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10599         }
10600     }
10601
10602     if(this.recordType){
10603         this.fields = this.recordType.prototype.fields;
10604     }
10605     this.modified = [];
10606
10607     this.addEvents({
10608         /**
10609          * @event datachanged
10610          * Fires when the data cache has changed, and a widget which is using this Store
10611          * as a Record cache should refresh its view.
10612          * @param {Store} this
10613          */
10614         datachanged : true,
10615         /**
10616          * @event metachange
10617          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10618          * @param {Store} this
10619          * @param {Object} meta The JSON metadata
10620          */
10621         metachange : true,
10622         /**
10623          * @event add
10624          * Fires when Records have been added to the Store
10625          * @param {Store} this
10626          * @param {Roo.data.Record[]} records The array of Records added
10627          * @param {Number} index The index at which the record(s) were added
10628          */
10629         add : true,
10630         /**
10631          * @event remove
10632          * Fires when a Record has been removed from the Store
10633          * @param {Store} this
10634          * @param {Roo.data.Record} record The Record that was removed
10635          * @param {Number} index The index at which the record was removed
10636          */
10637         remove : true,
10638         /**
10639          * @event update
10640          * Fires when a Record has been updated
10641          * @param {Store} this
10642          * @param {Roo.data.Record} record The Record that was updated
10643          * @param {String} operation The update operation being performed.  Value may be one of:
10644          * <pre><code>
10645  Roo.data.Record.EDIT
10646  Roo.data.Record.REJECT
10647  Roo.data.Record.COMMIT
10648          * </code></pre>
10649          */
10650         update : true,
10651         /**
10652          * @event clear
10653          * Fires when the data cache has been cleared.
10654          * @param {Store} this
10655          */
10656         clear : true,
10657         /**
10658          * @event beforeload
10659          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10660          * the load action will be canceled.
10661          * @param {Store} this
10662          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10663          */
10664         beforeload : true,
10665         /**
10666          * @event beforeloadadd
10667          * Fires after a new set of Records has been loaded.
10668          * @param {Store} this
10669          * @param {Roo.data.Record[]} records The Records that were loaded
10670          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10671          */
10672         beforeloadadd : true,
10673         /**
10674          * @event load
10675          * Fires after a new set of Records has been loaded, before they are added to the store.
10676          * @param {Store} this
10677          * @param {Roo.data.Record[]} records The Records that were loaded
10678          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10679          * @params {Object} return from reader
10680          */
10681         load : true,
10682         /**
10683          * @event loadexception
10684          * Fires if an exception occurs in the Proxy during loading.
10685          * Called with the signature of the Proxy's "loadexception" event.
10686          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10687          * 
10688          * @param {Proxy} 
10689          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10690          * @param {Object} load options 
10691          * @param {Object} jsonData from your request (normally this contains the Exception)
10692          */
10693         loadexception : true
10694     });
10695     
10696     if(this.proxy){
10697         this.proxy = Roo.factory(this.proxy, Roo.data);
10698         this.proxy.xmodule = this.xmodule || false;
10699         this.relayEvents(this.proxy,  ["loadexception"]);
10700     }
10701     this.sortToggle = {};
10702     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10703
10704     Roo.data.Store.superclass.constructor.call(this);
10705
10706     if(this.inlineData){
10707         this.loadData(this.inlineData);
10708         delete this.inlineData;
10709     }
10710 };
10711
10712 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10713      /**
10714     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10715     * without a remote query - used by combo/forms at present.
10716     */
10717     
10718     /**
10719     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10720     */
10721     /**
10722     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10723     */
10724     /**
10725     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10726     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10727     */
10728     /**
10729     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10730     * on any HTTP request
10731     */
10732     /**
10733     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10734     */
10735     /**
10736     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10737     */
10738     multiSort: false,
10739     /**
10740     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10741     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10742     */
10743     remoteSort : false,
10744
10745     /**
10746     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10747      * loaded or when a record is removed. (defaults to false).
10748     */
10749     pruneModifiedRecords : false,
10750
10751     // private
10752     lastOptions : null,
10753
10754     /**
10755      * Add Records to the Store and fires the add event.
10756      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10757      */
10758     add : function(records){
10759         records = [].concat(records);
10760         for(var i = 0, len = records.length; i < len; i++){
10761             records[i].join(this);
10762         }
10763         var index = this.data.length;
10764         this.data.addAll(records);
10765         this.fireEvent("add", this, records, index);
10766     },
10767
10768     /**
10769      * Remove a Record from the Store and fires the remove event.
10770      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10771      */
10772     remove : function(record){
10773         var index = this.data.indexOf(record);
10774         this.data.removeAt(index);
10775         if(this.pruneModifiedRecords){
10776             this.modified.remove(record);
10777         }
10778         this.fireEvent("remove", this, record, index);
10779     },
10780
10781     /**
10782      * Remove all Records from the Store and fires the clear event.
10783      */
10784     removeAll : function(){
10785         this.data.clear();
10786         if(this.pruneModifiedRecords){
10787             this.modified = [];
10788         }
10789         this.fireEvent("clear", this);
10790     },
10791
10792     /**
10793      * Inserts Records to the Store at the given index and fires the add event.
10794      * @param {Number} index The start index at which to insert the passed Records.
10795      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10796      */
10797     insert : function(index, records){
10798         records = [].concat(records);
10799         for(var i = 0, len = records.length; i < len; i++){
10800             this.data.insert(index, records[i]);
10801             records[i].join(this);
10802         }
10803         this.fireEvent("add", this, records, index);
10804     },
10805
10806     /**
10807      * Get the index within the cache of the passed Record.
10808      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10809      * @return {Number} The index of the passed Record. Returns -1 if not found.
10810      */
10811     indexOf : function(record){
10812         return this.data.indexOf(record);
10813     },
10814
10815     /**
10816      * Get the index within the cache of the Record with the passed id.
10817      * @param {String} id The id of the Record to find.
10818      * @return {Number} The index of the Record. Returns -1 if not found.
10819      */
10820     indexOfId : function(id){
10821         return this.data.indexOfKey(id);
10822     },
10823
10824     /**
10825      * Get the Record with the specified id.
10826      * @param {String} id The id of the Record to find.
10827      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10828      */
10829     getById : function(id){
10830         return this.data.key(id);
10831     },
10832
10833     /**
10834      * Get the Record at the specified index.
10835      * @param {Number} index The index of the Record to find.
10836      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10837      */
10838     getAt : function(index){
10839         return this.data.itemAt(index);
10840     },
10841
10842     /**
10843      * Returns a range of Records between specified indices.
10844      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10845      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10846      * @return {Roo.data.Record[]} An array of Records
10847      */
10848     getRange : function(start, end){
10849         return this.data.getRange(start, end);
10850     },
10851
10852     // private
10853     storeOptions : function(o){
10854         o = Roo.apply({}, o);
10855         delete o.callback;
10856         delete o.scope;
10857         this.lastOptions = o;
10858     },
10859
10860     /**
10861      * Loads the Record cache from the configured Proxy using the configured Reader.
10862      * <p>
10863      * If using remote paging, then the first load call must specify the <em>start</em>
10864      * and <em>limit</em> properties in the options.params property to establish the initial
10865      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10866      * <p>
10867      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10868      * and this call will return before the new data has been loaded. Perform any post-processing
10869      * in a callback function, or in a "load" event handler.</strong>
10870      * <p>
10871      * @param {Object} options An object containing properties which control loading options:<ul>
10872      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10873      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10874      * passed the following arguments:<ul>
10875      * <li>r : Roo.data.Record[]</li>
10876      * <li>options: Options object from the load call</li>
10877      * <li>success: Boolean success indicator</li></ul></li>
10878      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10879      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10880      * </ul>
10881      */
10882     load : function(options){
10883         options = options || {};
10884         if(this.fireEvent("beforeload", this, options) !== false){
10885             this.storeOptions(options);
10886             var p = Roo.apply(options.params || {}, this.baseParams);
10887             // if meta was not loaded from remote source.. try requesting it.
10888             if (!this.reader.metaFromRemote) {
10889                 p._requestMeta = 1;
10890             }
10891             if(this.sortInfo && this.remoteSort){
10892                 var pn = this.paramNames;
10893                 p[pn["sort"]] = this.sortInfo.field;
10894                 p[pn["dir"]] = this.sortInfo.direction;
10895             }
10896             if (this.multiSort) {
10897                 var pn = this.paramNames;
10898                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10899             }
10900             
10901             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10902         }
10903     },
10904
10905     /**
10906      * Reloads the Record cache from the configured Proxy using the configured Reader and
10907      * the options from the last load operation performed.
10908      * @param {Object} options (optional) An object containing properties which may override the options
10909      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10910      * the most recently used options are reused).
10911      */
10912     reload : function(options){
10913         this.load(Roo.applyIf(options||{}, this.lastOptions));
10914     },
10915
10916     // private
10917     // Called as a callback by the Reader during a load operation.
10918     loadRecords : function(o, options, success){
10919         if(!o || success === false){
10920             if(success !== false){
10921                 this.fireEvent("load", this, [], options, o);
10922             }
10923             if(options.callback){
10924                 options.callback.call(options.scope || this, [], options, false);
10925             }
10926             return;
10927         }
10928         // if data returned failure - throw an exception.
10929         if (o.success === false) {
10930             // show a message if no listener is registered.
10931             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10932                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10933             }
10934             // loadmask wil be hooked into this..
10935             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10936             return;
10937         }
10938         var r = o.records, t = o.totalRecords || r.length;
10939         
10940         this.fireEvent("beforeloadadd", this, r, options, o);
10941         
10942         if(!options || options.add !== true){
10943             if(this.pruneModifiedRecords){
10944                 this.modified = [];
10945             }
10946             for(var i = 0, len = r.length; i < len; i++){
10947                 r[i].join(this);
10948             }
10949             if(this.snapshot){
10950                 this.data = this.snapshot;
10951                 delete this.snapshot;
10952             }
10953             this.data.clear();
10954             this.data.addAll(r);
10955             this.totalLength = t;
10956             this.applySort();
10957             this.fireEvent("datachanged", this);
10958         }else{
10959             this.totalLength = Math.max(t, this.data.length+r.length);
10960             this.add(r);
10961         }
10962         this.fireEvent("load", this, r, options, o);
10963         if(options.callback){
10964             options.callback.call(options.scope || this, r, options, true);
10965         }
10966     },
10967
10968
10969     /**
10970      * Loads data from a passed data block. A Reader which understands the format of the data
10971      * must have been configured in the constructor.
10972      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10973      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10974      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10975      */
10976     loadData : function(o, append){
10977         var r = this.reader.readRecords(o);
10978         this.loadRecords(r, {add: append}, true);
10979     },
10980
10981     /**
10982      * Gets the number of cached records.
10983      * <p>
10984      * <em>If using paging, this may not be the total size of the dataset. If the data object
10985      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10986      * the data set size</em>
10987      */
10988     getCount : function(){
10989         return this.data.length || 0;
10990     },
10991
10992     /**
10993      * Gets the total number of records in the dataset as returned by the server.
10994      * <p>
10995      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10996      * the dataset size</em>
10997      */
10998     getTotalCount : function(){
10999         return this.totalLength || 0;
11000     },
11001
11002     /**
11003      * Returns the sort state of the Store as an object with two properties:
11004      * <pre><code>
11005  field {String} The name of the field by which the Records are sorted
11006  direction {String} The sort order, "ASC" or "DESC"
11007      * </code></pre>
11008      */
11009     getSortState : function(){
11010         return this.sortInfo;
11011     },
11012
11013     // private
11014     applySort : function(){
11015         if(this.sortInfo && !this.remoteSort){
11016             var s = this.sortInfo, f = s.field;
11017             var st = this.fields.get(f).sortType;
11018             var fn = function(r1, r2){
11019                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11020                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11021             };
11022             this.data.sort(s.direction, fn);
11023             if(this.snapshot && this.snapshot != this.data){
11024                 this.snapshot.sort(s.direction, fn);
11025             }
11026         }
11027     },
11028
11029     /**
11030      * Sets the default sort column and order to be used by the next load operation.
11031      * @param {String} fieldName The name of the field to sort by.
11032      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11033      */
11034     setDefaultSort : function(field, dir){
11035         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11036     },
11037
11038     /**
11039      * Sort the Records.
11040      * If remote sorting is used, the sort is performed on the server, and the cache is
11041      * reloaded. If local sorting is used, the cache is sorted internally.
11042      * @param {String} fieldName The name of the field to sort by.
11043      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11044      */
11045     sort : function(fieldName, dir){
11046         var f = this.fields.get(fieldName);
11047         if(!dir){
11048             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11049             
11050             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11051                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11052             }else{
11053                 dir = f.sortDir;
11054             }
11055         }
11056         this.sortToggle[f.name] = dir;
11057         this.sortInfo = {field: f.name, direction: dir};
11058         if(!this.remoteSort){
11059             this.applySort();
11060             this.fireEvent("datachanged", this);
11061         }else{
11062             this.load(this.lastOptions);
11063         }
11064     },
11065
11066     /**
11067      * Calls the specified function for each of the Records in the cache.
11068      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11069      * Returning <em>false</em> aborts and exits the iteration.
11070      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11071      */
11072     each : function(fn, scope){
11073         this.data.each(fn, scope);
11074     },
11075
11076     /**
11077      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11078      * (e.g., during paging).
11079      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11080      */
11081     getModifiedRecords : function(){
11082         return this.modified;
11083     },
11084
11085     // private
11086     createFilterFn : function(property, value, anyMatch){
11087         if(!value.exec){ // not a regex
11088             value = String(value);
11089             if(value.length == 0){
11090                 return false;
11091             }
11092             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11093         }
11094         return function(r){
11095             return value.test(r.data[property]);
11096         };
11097     },
11098
11099     /**
11100      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11101      * @param {String} property A field on your records
11102      * @param {Number} start The record index to start at (defaults to 0)
11103      * @param {Number} end The last record index to include (defaults to length - 1)
11104      * @return {Number} The sum
11105      */
11106     sum : function(property, start, end){
11107         var rs = this.data.items, v = 0;
11108         start = start || 0;
11109         end = (end || end === 0) ? end : rs.length-1;
11110
11111         for(var i = start; i <= end; i++){
11112             v += (rs[i].data[property] || 0);
11113         }
11114         return v;
11115     },
11116
11117     /**
11118      * Filter the records by a specified property.
11119      * @param {String} field A field on your records
11120      * @param {String/RegExp} value Either a string that the field
11121      * should start with or a RegExp to test against the field
11122      * @param {Boolean} anyMatch True to match any part not just the beginning
11123      */
11124     filter : function(property, value, anyMatch){
11125         var fn = this.createFilterFn(property, value, anyMatch);
11126         return fn ? this.filterBy(fn) : this.clearFilter();
11127     },
11128
11129     /**
11130      * Filter by a function. The specified function will be called with each
11131      * record in this data source. If the function returns true the record is included,
11132      * otherwise it is filtered.
11133      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11134      * @param {Object} scope (optional) The scope of the function (defaults to this)
11135      */
11136     filterBy : function(fn, scope){
11137         this.snapshot = this.snapshot || this.data;
11138         this.data = this.queryBy(fn, scope||this);
11139         this.fireEvent("datachanged", this);
11140     },
11141
11142     /**
11143      * Query the records by a specified property.
11144      * @param {String} field A field on your records
11145      * @param {String/RegExp} value Either a string that the field
11146      * should start with or a RegExp to test against the field
11147      * @param {Boolean} anyMatch True to match any part not just the beginning
11148      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11149      */
11150     query : function(property, value, anyMatch){
11151         var fn = this.createFilterFn(property, value, anyMatch);
11152         return fn ? this.queryBy(fn) : this.data.clone();
11153     },
11154
11155     /**
11156      * Query by a function. The specified function will be called with each
11157      * record in this data source. If the function returns true the record is included
11158      * in the results.
11159      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11160      * @param {Object} scope (optional) The scope of the function (defaults to this)
11161       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11162      **/
11163     queryBy : function(fn, scope){
11164         var data = this.snapshot || this.data;
11165         return data.filterBy(fn, scope||this);
11166     },
11167
11168     /**
11169      * Collects unique values for a particular dataIndex from this store.
11170      * @param {String} dataIndex The property to collect
11171      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11172      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11173      * @return {Array} An array of the unique values
11174      **/
11175     collect : function(dataIndex, allowNull, bypassFilter){
11176         var d = (bypassFilter === true && this.snapshot) ?
11177                 this.snapshot.items : this.data.items;
11178         var v, sv, r = [], l = {};
11179         for(var i = 0, len = d.length; i < len; i++){
11180             v = d[i].data[dataIndex];
11181             sv = String(v);
11182             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11183                 l[sv] = true;
11184                 r[r.length] = v;
11185             }
11186         }
11187         return r;
11188     },
11189
11190     /**
11191      * Revert to a view of the Record cache with no filtering applied.
11192      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11193      */
11194     clearFilter : function(suppressEvent){
11195         if(this.snapshot && this.snapshot != this.data){
11196             this.data = this.snapshot;
11197             delete this.snapshot;
11198             if(suppressEvent !== true){
11199                 this.fireEvent("datachanged", this);
11200             }
11201         }
11202     },
11203
11204     // private
11205     afterEdit : function(record){
11206         if(this.modified.indexOf(record) == -1){
11207             this.modified.push(record);
11208         }
11209         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11210     },
11211     
11212     // private
11213     afterReject : function(record){
11214         this.modified.remove(record);
11215         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11216     },
11217
11218     // private
11219     afterCommit : function(record){
11220         this.modified.remove(record);
11221         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11222     },
11223
11224     /**
11225      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11226      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11227      */
11228     commitChanges : function(){
11229         var m = this.modified.slice(0);
11230         this.modified = [];
11231         for(var i = 0, len = m.length; i < len; i++){
11232             m[i].commit();
11233         }
11234     },
11235
11236     /**
11237      * Cancel outstanding changes on all changed records.
11238      */
11239     rejectChanges : function(){
11240         var m = this.modified.slice(0);
11241         this.modified = [];
11242         for(var i = 0, len = m.length; i < len; i++){
11243             m[i].reject();
11244         }
11245     },
11246
11247     onMetaChange : function(meta, rtype, o){
11248         this.recordType = rtype;
11249         this.fields = rtype.prototype.fields;
11250         delete this.snapshot;
11251         this.sortInfo = meta.sortInfo || this.sortInfo;
11252         this.modified = [];
11253         this.fireEvent('metachange', this, this.reader.meta);
11254     },
11255     
11256     moveIndex : function(data, type)
11257     {
11258         var index = this.indexOf(data);
11259         
11260         var newIndex = index + type;
11261         
11262         this.remove(data);
11263         
11264         this.insert(newIndex, data);
11265         
11266     }
11267 });/*
11268  * Based on:
11269  * Ext JS Library 1.1.1
11270  * Copyright(c) 2006-2007, Ext JS, LLC.
11271  *
11272  * Originally Released Under LGPL - original licence link has changed is not relivant.
11273  *
11274  * Fork - LGPL
11275  * <script type="text/javascript">
11276  */
11277
11278 /**
11279  * @class Roo.data.SimpleStore
11280  * @extends Roo.data.Store
11281  * Small helper class to make creating Stores from Array data easier.
11282  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11283  * @cfg {Array} fields An array of field definition objects, or field name strings.
11284  * @cfg {Array} data The multi-dimensional array of data
11285  * @constructor
11286  * @param {Object} config
11287  */
11288 Roo.data.SimpleStore = function(config){
11289     Roo.data.SimpleStore.superclass.constructor.call(this, {
11290         isLocal : true,
11291         reader: new Roo.data.ArrayReader({
11292                 id: config.id
11293             },
11294             Roo.data.Record.create(config.fields)
11295         ),
11296         proxy : new Roo.data.MemoryProxy(config.data)
11297     });
11298     this.load();
11299 };
11300 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11301  * Based on:
11302  * Ext JS Library 1.1.1
11303  * Copyright(c) 2006-2007, Ext JS, LLC.
11304  *
11305  * Originally Released Under LGPL - original licence link has changed is not relivant.
11306  *
11307  * Fork - LGPL
11308  * <script type="text/javascript">
11309  */
11310
11311 /**
11312 /**
11313  * @extends Roo.data.Store
11314  * @class Roo.data.JsonStore
11315  * Small helper class to make creating Stores for JSON data easier. <br/>
11316 <pre><code>
11317 var store = new Roo.data.JsonStore({
11318     url: 'get-images.php',
11319     root: 'images',
11320     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11321 });
11322 </code></pre>
11323  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11324  * JsonReader and HttpProxy (unless inline data is provided).</b>
11325  * @cfg {Array} fields An array of field definition objects, or field name strings.
11326  * @constructor
11327  * @param {Object} config
11328  */
11329 Roo.data.JsonStore = function(c){
11330     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11331         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11332         reader: new Roo.data.JsonReader(c, c.fields)
11333     }));
11334 };
11335 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11336  * Based on:
11337  * Ext JS Library 1.1.1
11338  * Copyright(c) 2006-2007, Ext JS, LLC.
11339  *
11340  * Originally Released Under LGPL - original licence link has changed is not relivant.
11341  *
11342  * Fork - LGPL
11343  * <script type="text/javascript">
11344  */
11345
11346  
11347 Roo.data.Field = function(config){
11348     if(typeof config == "string"){
11349         config = {name: config};
11350     }
11351     Roo.apply(this, config);
11352     
11353     if(!this.type){
11354         this.type = "auto";
11355     }
11356     
11357     var st = Roo.data.SortTypes;
11358     // named sortTypes are supported, here we look them up
11359     if(typeof this.sortType == "string"){
11360         this.sortType = st[this.sortType];
11361     }
11362     
11363     // set default sortType for strings and dates
11364     if(!this.sortType){
11365         switch(this.type){
11366             case "string":
11367                 this.sortType = st.asUCString;
11368                 break;
11369             case "date":
11370                 this.sortType = st.asDate;
11371                 break;
11372             default:
11373                 this.sortType = st.none;
11374         }
11375     }
11376
11377     // define once
11378     var stripRe = /[\$,%]/g;
11379
11380     // prebuilt conversion function for this field, instead of
11381     // switching every time we're reading a value
11382     if(!this.convert){
11383         var cv, dateFormat = this.dateFormat;
11384         switch(this.type){
11385             case "":
11386             case "auto":
11387             case undefined:
11388                 cv = function(v){ return v; };
11389                 break;
11390             case "string":
11391                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11392                 break;
11393             case "int":
11394                 cv = function(v){
11395                     return v !== undefined && v !== null && v !== '' ?
11396                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11397                     };
11398                 break;
11399             case "float":
11400                 cv = function(v){
11401                     return v !== undefined && v !== null && v !== '' ?
11402                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11403                     };
11404                 break;
11405             case "bool":
11406             case "boolean":
11407                 cv = function(v){ return v === true || v === "true" || v == 1; };
11408                 break;
11409             case "date":
11410                 cv = function(v){
11411                     if(!v){
11412                         return '';
11413                     }
11414                     if(v instanceof Date){
11415                         return v;
11416                     }
11417                     if(dateFormat){
11418                         if(dateFormat == "timestamp"){
11419                             return new Date(v*1000);
11420                         }
11421                         return Date.parseDate(v, dateFormat);
11422                     }
11423                     var parsed = Date.parse(v);
11424                     return parsed ? new Date(parsed) : null;
11425                 };
11426              break;
11427             
11428         }
11429         this.convert = cv;
11430     }
11431 };
11432
11433 Roo.data.Field.prototype = {
11434     dateFormat: null,
11435     defaultValue: "",
11436     mapping: null,
11437     sortType : null,
11438     sortDir : "ASC"
11439 };/*
11440  * Based on:
11441  * Ext JS Library 1.1.1
11442  * Copyright(c) 2006-2007, Ext JS, LLC.
11443  *
11444  * Originally Released Under LGPL - original licence link has changed is not relivant.
11445  *
11446  * Fork - LGPL
11447  * <script type="text/javascript">
11448  */
11449  
11450 // Base class for reading structured data from a data source.  This class is intended to be
11451 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11452
11453 /**
11454  * @class Roo.data.DataReader
11455  * Base class for reading structured data from a data source.  This class is intended to be
11456  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11457  */
11458
11459 Roo.data.DataReader = function(meta, recordType){
11460     
11461     this.meta = meta;
11462     
11463     this.recordType = recordType instanceof Array ? 
11464         Roo.data.Record.create(recordType) : recordType;
11465 };
11466
11467 Roo.data.DataReader.prototype = {
11468      /**
11469      * Create an empty record
11470      * @param {Object} data (optional) - overlay some values
11471      * @return {Roo.data.Record} record created.
11472      */
11473     newRow :  function(d) {
11474         var da =  {};
11475         this.recordType.prototype.fields.each(function(c) {
11476             switch( c.type) {
11477                 case 'int' : da[c.name] = 0; break;
11478                 case 'date' : da[c.name] = new Date(); break;
11479                 case 'float' : da[c.name] = 0.0; break;
11480                 case 'boolean' : da[c.name] = false; break;
11481                 default : da[c.name] = ""; break;
11482             }
11483             
11484         });
11485         return new this.recordType(Roo.apply(da, d));
11486     }
11487     
11488 };/*
11489  * Based on:
11490  * Ext JS Library 1.1.1
11491  * Copyright(c) 2006-2007, Ext JS, LLC.
11492  *
11493  * Originally Released Under LGPL - original licence link has changed is not relivant.
11494  *
11495  * Fork - LGPL
11496  * <script type="text/javascript">
11497  */
11498
11499 /**
11500  * @class Roo.data.DataProxy
11501  * @extends Roo.data.Observable
11502  * This class is an abstract base class for implementations which provide retrieval of
11503  * unformatted data objects.<br>
11504  * <p>
11505  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11506  * (of the appropriate type which knows how to parse the data object) to provide a block of
11507  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11508  * <p>
11509  * Custom implementations must implement the load method as described in
11510  * {@link Roo.data.HttpProxy#load}.
11511  */
11512 Roo.data.DataProxy = function(){
11513     this.addEvents({
11514         /**
11515          * @event beforeload
11516          * Fires before a network request is made to retrieve a data object.
11517          * @param {Object} This DataProxy object.
11518          * @param {Object} params The params parameter to the load function.
11519          */
11520         beforeload : true,
11521         /**
11522          * @event load
11523          * Fires before the load method's callback is called.
11524          * @param {Object} This DataProxy object.
11525          * @param {Object} o The data object.
11526          * @param {Object} arg The callback argument object passed to the load function.
11527          */
11528         load : true,
11529         /**
11530          * @event loadexception
11531          * Fires if an Exception occurs during data retrieval.
11532          * @param {Object} This DataProxy object.
11533          * @param {Object} o The data object.
11534          * @param {Object} arg The callback argument object passed to the load function.
11535          * @param {Object} e The Exception.
11536          */
11537         loadexception : true
11538     });
11539     Roo.data.DataProxy.superclass.constructor.call(this);
11540 };
11541
11542 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11543
11544     /**
11545      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11546      */
11547 /*
11548  * Based on:
11549  * Ext JS Library 1.1.1
11550  * Copyright(c) 2006-2007, Ext JS, LLC.
11551  *
11552  * Originally Released Under LGPL - original licence link has changed is not relivant.
11553  *
11554  * Fork - LGPL
11555  * <script type="text/javascript">
11556  */
11557 /**
11558  * @class Roo.data.MemoryProxy
11559  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11560  * to the Reader when its load method is called.
11561  * @constructor
11562  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11563  */
11564 Roo.data.MemoryProxy = function(data){
11565     if (data.data) {
11566         data = data.data;
11567     }
11568     Roo.data.MemoryProxy.superclass.constructor.call(this);
11569     this.data = data;
11570 };
11571
11572 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11573     
11574     /**
11575      * Load data from the requested source (in this case an in-memory
11576      * data object passed to the constructor), read the data object into
11577      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11578      * process that block using the passed callback.
11579      * @param {Object} params This parameter is not used by the MemoryProxy class.
11580      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11581      * object into a block of Roo.data.Records.
11582      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11583      * The function must be passed <ul>
11584      * <li>The Record block object</li>
11585      * <li>The "arg" argument from the load function</li>
11586      * <li>A boolean success indicator</li>
11587      * </ul>
11588      * @param {Object} scope The scope in which to call the callback
11589      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11590      */
11591     load : function(params, reader, callback, scope, arg){
11592         params = params || {};
11593         var result;
11594         try {
11595             result = reader.readRecords(this.data);
11596         }catch(e){
11597             this.fireEvent("loadexception", this, arg, null, e);
11598             callback.call(scope, null, arg, false);
11599             return;
11600         }
11601         callback.call(scope, result, arg, true);
11602     },
11603     
11604     // private
11605     update : function(params, records){
11606         
11607     }
11608 });/*
11609  * Based on:
11610  * Ext JS Library 1.1.1
11611  * Copyright(c) 2006-2007, Ext JS, LLC.
11612  *
11613  * Originally Released Under LGPL - original licence link has changed is not relivant.
11614  *
11615  * Fork - LGPL
11616  * <script type="text/javascript">
11617  */
11618 /**
11619  * @class Roo.data.HttpProxy
11620  * @extends Roo.data.DataProxy
11621  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11622  * configured to reference a certain URL.<br><br>
11623  * <p>
11624  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11625  * from which the running page was served.<br><br>
11626  * <p>
11627  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11628  * <p>
11629  * Be aware that to enable the browser to parse an XML document, the server must set
11630  * the Content-Type header in the HTTP response to "text/xml".
11631  * @constructor
11632  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11633  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11634  * will be used to make the request.
11635  */
11636 Roo.data.HttpProxy = function(conn){
11637     Roo.data.HttpProxy.superclass.constructor.call(this);
11638     // is conn a conn config or a real conn?
11639     this.conn = conn;
11640     this.useAjax = !conn || !conn.events;
11641   
11642 };
11643
11644 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11645     // thse are take from connection...
11646     
11647     /**
11648      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11649      */
11650     /**
11651      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11652      * extra parameters to each request made by this object. (defaults to undefined)
11653      */
11654     /**
11655      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11656      *  to each request made by this object. (defaults to undefined)
11657      */
11658     /**
11659      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11660      */
11661     /**
11662      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11663      */
11664      /**
11665      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11666      * @type Boolean
11667      */
11668   
11669
11670     /**
11671      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11672      * @type Boolean
11673      */
11674     /**
11675      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11676      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11677      * a finer-grained basis than the DataProxy events.
11678      */
11679     getConnection : function(){
11680         return this.useAjax ? Roo.Ajax : this.conn;
11681     },
11682
11683     /**
11684      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11685      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11686      * process that block using the passed callback.
11687      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11688      * for the request to the remote server.
11689      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11690      * object into a block of Roo.data.Records.
11691      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11692      * The function must be passed <ul>
11693      * <li>The Record block object</li>
11694      * <li>The "arg" argument from the load function</li>
11695      * <li>A boolean success indicator</li>
11696      * </ul>
11697      * @param {Object} scope The scope in which to call the callback
11698      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11699      */
11700     load : function(params, reader, callback, scope, arg){
11701         if(this.fireEvent("beforeload", this, params) !== false){
11702             var  o = {
11703                 params : params || {},
11704                 request: {
11705                     callback : callback,
11706                     scope : scope,
11707                     arg : arg
11708                 },
11709                 reader: reader,
11710                 callback : this.loadResponse,
11711                 scope: this
11712             };
11713             if(this.useAjax){
11714                 Roo.applyIf(o, this.conn);
11715                 if(this.activeRequest){
11716                     Roo.Ajax.abort(this.activeRequest);
11717                 }
11718                 this.activeRequest = Roo.Ajax.request(o);
11719             }else{
11720                 this.conn.request(o);
11721             }
11722         }else{
11723             callback.call(scope||this, null, arg, false);
11724         }
11725     },
11726
11727     // private
11728     loadResponse : function(o, success, response){
11729         delete this.activeRequest;
11730         if(!success){
11731             this.fireEvent("loadexception", this, o, response);
11732             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11733             return;
11734         }
11735         var result;
11736         try {
11737             result = o.reader.read(response);
11738         }catch(e){
11739             this.fireEvent("loadexception", this, o, response, e);
11740             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11741             return;
11742         }
11743         
11744         this.fireEvent("load", this, o, o.request.arg);
11745         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11746     },
11747
11748     // private
11749     update : function(dataSet){
11750
11751     },
11752
11753     // private
11754     updateResponse : function(dataSet){
11755
11756     }
11757 });/*
11758  * Based on:
11759  * Ext JS Library 1.1.1
11760  * Copyright(c) 2006-2007, Ext JS, LLC.
11761  *
11762  * Originally Released Under LGPL - original licence link has changed is not relivant.
11763  *
11764  * Fork - LGPL
11765  * <script type="text/javascript">
11766  */
11767
11768 /**
11769  * @class Roo.data.ScriptTagProxy
11770  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11771  * other than the originating domain of the running page.<br><br>
11772  * <p>
11773  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
11774  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11775  * <p>
11776  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11777  * source code that is used as the source inside a &lt;script> tag.<br><br>
11778  * <p>
11779  * In order for the browser to process the returned data, the server must wrap the data object
11780  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11781  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11782  * depending on whether the callback name was passed:
11783  * <p>
11784  * <pre><code>
11785 boolean scriptTag = false;
11786 String cb = request.getParameter("callback");
11787 if (cb != null) {
11788     scriptTag = true;
11789     response.setContentType("text/javascript");
11790 } else {
11791     response.setContentType("application/x-json");
11792 }
11793 Writer out = response.getWriter();
11794 if (scriptTag) {
11795     out.write(cb + "(");
11796 }
11797 out.print(dataBlock.toJsonString());
11798 if (scriptTag) {
11799     out.write(");");
11800 }
11801 </pre></code>
11802  *
11803  * @constructor
11804  * @param {Object} config A configuration object.
11805  */
11806 Roo.data.ScriptTagProxy = function(config){
11807     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11808     Roo.apply(this, config);
11809     this.head = document.getElementsByTagName("head")[0];
11810 };
11811
11812 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11813
11814 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11815     /**
11816      * @cfg {String} url The URL from which to request the data object.
11817      */
11818     /**
11819      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11820      */
11821     timeout : 30000,
11822     /**
11823      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11824      * the server the name of the callback function set up by the load call to process the returned data object.
11825      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11826      * javascript output which calls this named function passing the data object as its only parameter.
11827      */
11828     callbackParam : "callback",
11829     /**
11830      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11831      * name to the request.
11832      */
11833     nocache : true,
11834
11835     /**
11836      * Load data from the configured URL, read the data object into
11837      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11838      * process that block using the passed callback.
11839      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11840      * for the request to the remote server.
11841      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11842      * object into a block of Roo.data.Records.
11843      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11844      * The function must be passed <ul>
11845      * <li>The Record block object</li>
11846      * <li>The "arg" argument from the load function</li>
11847      * <li>A boolean success indicator</li>
11848      * </ul>
11849      * @param {Object} scope The scope in which to call the callback
11850      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11851      */
11852     load : function(params, reader, callback, scope, arg){
11853         if(this.fireEvent("beforeload", this, params) !== false){
11854
11855             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11856
11857             var url = this.url;
11858             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11859             if(this.nocache){
11860                 url += "&_dc=" + (new Date().getTime());
11861             }
11862             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11863             var trans = {
11864                 id : transId,
11865                 cb : "stcCallback"+transId,
11866                 scriptId : "stcScript"+transId,
11867                 params : params,
11868                 arg : arg,
11869                 url : url,
11870                 callback : callback,
11871                 scope : scope,
11872                 reader : reader
11873             };
11874             var conn = this;
11875
11876             window[trans.cb] = function(o){
11877                 conn.handleResponse(o, trans);
11878             };
11879
11880             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11881
11882             if(this.autoAbort !== false){
11883                 this.abort();
11884             }
11885
11886             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11887
11888             var script = document.createElement("script");
11889             script.setAttribute("src", url);
11890             script.setAttribute("type", "text/javascript");
11891             script.setAttribute("id", trans.scriptId);
11892             this.head.appendChild(script);
11893
11894             this.trans = trans;
11895         }else{
11896             callback.call(scope||this, null, arg, false);
11897         }
11898     },
11899
11900     // private
11901     isLoading : function(){
11902         return this.trans ? true : false;
11903     },
11904
11905     /**
11906      * Abort the current server request.
11907      */
11908     abort : function(){
11909         if(this.isLoading()){
11910             this.destroyTrans(this.trans);
11911         }
11912     },
11913
11914     // private
11915     destroyTrans : function(trans, isLoaded){
11916         this.head.removeChild(document.getElementById(trans.scriptId));
11917         clearTimeout(trans.timeoutId);
11918         if(isLoaded){
11919             window[trans.cb] = undefined;
11920             try{
11921                 delete window[trans.cb];
11922             }catch(e){}
11923         }else{
11924             // if hasn't been loaded, wait for load to remove it to prevent script error
11925             window[trans.cb] = function(){
11926                 window[trans.cb] = undefined;
11927                 try{
11928                     delete window[trans.cb];
11929                 }catch(e){}
11930             };
11931         }
11932     },
11933
11934     // private
11935     handleResponse : function(o, trans){
11936         this.trans = false;
11937         this.destroyTrans(trans, true);
11938         var result;
11939         try {
11940             result = trans.reader.readRecords(o);
11941         }catch(e){
11942             this.fireEvent("loadexception", this, o, trans.arg, e);
11943             trans.callback.call(trans.scope||window, null, trans.arg, false);
11944             return;
11945         }
11946         this.fireEvent("load", this, o, trans.arg);
11947         trans.callback.call(trans.scope||window, result, trans.arg, true);
11948     },
11949
11950     // private
11951     handleFailure : function(trans){
11952         this.trans = false;
11953         this.destroyTrans(trans, false);
11954         this.fireEvent("loadexception", this, null, trans.arg);
11955         trans.callback.call(trans.scope||window, null, trans.arg, false);
11956     }
11957 });/*
11958  * Based on:
11959  * Ext JS Library 1.1.1
11960  * Copyright(c) 2006-2007, Ext JS, LLC.
11961  *
11962  * Originally Released Under LGPL - original licence link has changed is not relivant.
11963  *
11964  * Fork - LGPL
11965  * <script type="text/javascript">
11966  */
11967
11968 /**
11969  * @class Roo.data.JsonReader
11970  * @extends Roo.data.DataReader
11971  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11972  * based on mappings in a provided Roo.data.Record constructor.
11973  * 
11974  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11975  * in the reply previously. 
11976  * 
11977  * <p>
11978  * Example code:
11979  * <pre><code>
11980 var RecordDef = Roo.data.Record.create([
11981     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11982     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11983 ]);
11984 var myReader = new Roo.data.JsonReader({
11985     totalProperty: "results",    // The property which contains the total dataset size (optional)
11986     root: "rows",                // The property which contains an Array of row objects
11987     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11988 }, RecordDef);
11989 </code></pre>
11990  * <p>
11991  * This would consume a JSON file like this:
11992  * <pre><code>
11993 { 'results': 2, 'rows': [
11994     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11995     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11996 }
11997 </code></pre>
11998  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11999  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12000  * paged from the remote server.
12001  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12002  * @cfg {String} root name of the property which contains the Array of row objects.
12003  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12004  * @cfg {Array} fields Array of field definition objects
12005  * @constructor
12006  * Create a new JsonReader
12007  * @param {Object} meta Metadata configuration options
12008  * @param {Object} recordType Either an Array of field definition objects,
12009  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12010  */
12011 Roo.data.JsonReader = function(meta, recordType){
12012     
12013     meta = meta || {};
12014     // set some defaults:
12015     Roo.applyIf(meta, {
12016         totalProperty: 'total',
12017         successProperty : 'success',
12018         root : 'data',
12019         id : 'id'
12020     });
12021     
12022     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12023 };
12024 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12025     
12026     /**
12027      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12028      * Used by Store query builder to append _requestMeta to params.
12029      * 
12030      */
12031     metaFromRemote : false,
12032     /**
12033      * This method is only used by a DataProxy which has retrieved data from a remote server.
12034      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12035      * @return {Object} data A data block which is used by an Roo.data.Store object as
12036      * a cache of Roo.data.Records.
12037      */
12038     read : function(response){
12039         var json = response.responseText;
12040        
12041         var o = /* eval:var:o */ eval("("+json+")");
12042         if(!o) {
12043             throw {message: "JsonReader.read: Json object not found"};
12044         }
12045         
12046         if(o.metaData){
12047             
12048             delete this.ef;
12049             this.metaFromRemote = true;
12050             this.meta = o.metaData;
12051             this.recordType = Roo.data.Record.create(o.metaData.fields);
12052             this.onMetaChange(this.meta, this.recordType, o);
12053         }
12054         return this.readRecords(o);
12055     },
12056
12057     // private function a store will implement
12058     onMetaChange : function(meta, recordType, o){
12059
12060     },
12061
12062     /**
12063          * @ignore
12064          */
12065     simpleAccess: function(obj, subsc) {
12066         return obj[subsc];
12067     },
12068
12069         /**
12070          * @ignore
12071          */
12072     getJsonAccessor: function(){
12073         var re = /[\[\.]/;
12074         return function(expr) {
12075             try {
12076                 return(re.test(expr))
12077                     ? new Function("obj", "return obj." + expr)
12078                     : function(obj){
12079                         return obj[expr];
12080                     };
12081             } catch(e){}
12082             return Roo.emptyFn;
12083         };
12084     }(),
12085
12086     /**
12087      * Create a data block containing Roo.data.Records from an XML document.
12088      * @param {Object} o An object which contains an Array of row objects in the property specified
12089      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12090      * which contains the total size of the dataset.
12091      * @return {Object} data A data block which is used by an Roo.data.Store object as
12092      * a cache of Roo.data.Records.
12093      */
12094     readRecords : function(o){
12095         /**
12096          * After any data loads, the raw JSON data is available for further custom processing.
12097          * @type Object
12098          */
12099         this.o = o;
12100         var s = this.meta, Record = this.recordType,
12101             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12102
12103 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12104         if (!this.ef) {
12105             if(s.totalProperty) {
12106                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12107                 }
12108                 if(s.successProperty) {
12109                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12110                 }
12111                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12112                 if (s.id) {
12113                         var g = this.getJsonAccessor(s.id);
12114                         this.getId = function(rec) {
12115                                 var r = g(rec);  
12116                                 return (r === undefined || r === "") ? null : r;
12117                         };
12118                 } else {
12119                         this.getId = function(){return null;};
12120                 }
12121             this.ef = [];
12122             for(var jj = 0; jj < fl; jj++){
12123                 f = fi[jj];
12124                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12125                 this.ef[jj] = this.getJsonAccessor(map);
12126             }
12127         }
12128
12129         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12130         if(s.totalProperty){
12131             var vt = parseInt(this.getTotal(o), 10);
12132             if(!isNaN(vt)){
12133                 totalRecords = vt;
12134             }
12135         }
12136         if(s.successProperty){
12137             var vs = this.getSuccess(o);
12138             if(vs === false || vs === 'false'){
12139                 success = false;
12140             }
12141         }
12142         var records = [];
12143         for(var i = 0; i < c; i++){
12144                 var n = root[i];
12145             var values = {};
12146             var id = this.getId(n);
12147             for(var j = 0; j < fl; j++){
12148                 f = fi[j];
12149             var v = this.ef[j](n);
12150             if (!f.convert) {
12151                 Roo.log('missing convert for ' + f.name);
12152                 Roo.log(f);
12153                 continue;
12154             }
12155             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12156             }
12157             var record = new Record(values, id);
12158             record.json = n;
12159             records[i] = record;
12160         }
12161         return {
12162             raw : o,
12163             success : success,
12164             records : records,
12165             totalRecords : totalRecords
12166         };
12167     }
12168 });/*
12169  * Based on:
12170  * Ext JS Library 1.1.1
12171  * Copyright(c) 2006-2007, Ext JS, LLC.
12172  *
12173  * Originally Released Under LGPL - original licence link has changed is not relivant.
12174  *
12175  * Fork - LGPL
12176  * <script type="text/javascript">
12177  */
12178
12179 /**
12180  * @class Roo.data.ArrayReader
12181  * @extends Roo.data.DataReader
12182  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12183  * Each element of that Array represents a row of data fields. The
12184  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12185  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12186  * <p>
12187  * Example code:.
12188  * <pre><code>
12189 var RecordDef = Roo.data.Record.create([
12190     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12191     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12192 ]);
12193 var myReader = new Roo.data.ArrayReader({
12194     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12195 }, RecordDef);
12196 </code></pre>
12197  * <p>
12198  * This would consume an Array like this:
12199  * <pre><code>
12200 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12201   </code></pre>
12202  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12203  * @constructor
12204  * Create a new JsonReader
12205  * @param {Object} meta Metadata configuration options.
12206  * @param {Object} recordType Either an Array of field definition objects
12207  * as specified to {@link Roo.data.Record#create},
12208  * or an {@link Roo.data.Record} object
12209  * created using {@link Roo.data.Record#create}.
12210  */
12211 Roo.data.ArrayReader = function(meta, recordType){
12212     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12213 };
12214
12215 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12216     /**
12217      * Create a data block containing Roo.data.Records from an XML document.
12218      * @param {Object} o An Array of row objects which represents the dataset.
12219      * @return {Object} data A data block which is used by an Roo.data.Store object as
12220      * a cache of Roo.data.Records.
12221      */
12222     readRecords : function(o){
12223         var sid = this.meta ? this.meta.id : null;
12224         var recordType = this.recordType, fields = recordType.prototype.fields;
12225         var records = [];
12226         var root = o;
12227             for(var i = 0; i < root.length; i++){
12228                     var n = root[i];
12229                 var values = {};
12230                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12231                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12232                 var f = fields.items[j];
12233                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12234                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12235                 v = f.convert(v);
12236                 values[f.name] = v;
12237             }
12238                 var record = new recordType(values, id);
12239                 record.json = n;
12240                 records[records.length] = record;
12241             }
12242             return {
12243                 records : records,
12244                 totalRecords : records.length
12245             };
12246     }
12247 });/*
12248  * - LGPL
12249  * * 
12250  */
12251
12252 /**
12253  * @class Roo.bootstrap.ComboBox
12254  * @extends Roo.bootstrap.TriggerField
12255  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12256  * @cfg {Boolean} append (true|false) default false
12257  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12258  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12259  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12260  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12261  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12262  * @cfg {Boolean} animate default true
12263  * @cfg {Boolean} emptyResultText only for touch device
12264  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12265  * @constructor
12266  * Create a new ComboBox.
12267  * @param {Object} config Configuration options
12268  */
12269 Roo.bootstrap.ComboBox = function(config){
12270     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12271     this.addEvents({
12272         /**
12273          * @event expand
12274          * Fires when the dropdown list is expanded
12275              * @param {Roo.bootstrap.ComboBox} combo This combo box
12276              */
12277         'expand' : true,
12278         /**
12279          * @event collapse
12280          * Fires when the dropdown list is collapsed
12281              * @param {Roo.bootstrap.ComboBox} combo This combo box
12282              */
12283         'collapse' : true,
12284         /**
12285          * @event beforeselect
12286          * Fires before a list item is selected. Return false to cancel the selection.
12287              * @param {Roo.bootstrap.ComboBox} combo This combo box
12288              * @param {Roo.data.Record} record The data record returned from the underlying store
12289              * @param {Number} index The index of the selected item in the dropdown list
12290              */
12291         'beforeselect' : true,
12292         /**
12293          * @event select
12294          * Fires when a list item is selected
12295              * @param {Roo.bootstrap.ComboBox} combo This combo box
12296              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12297              * @param {Number} index The index of the selected item in the dropdown list
12298              */
12299         'select' : true,
12300         /**
12301          * @event beforequery
12302          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12303          * The event object passed has these properties:
12304              * @param {Roo.bootstrap.ComboBox} combo This combo box
12305              * @param {String} query The query
12306              * @param {Boolean} forceAll true to force "all" query
12307              * @param {Boolean} cancel true to cancel the query
12308              * @param {Object} e The query event object
12309              */
12310         'beforequery': true,
12311          /**
12312          * @event add
12313          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12314              * @param {Roo.bootstrap.ComboBox} combo This combo box
12315              */
12316         'add' : true,
12317         /**
12318          * @event edit
12319          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12320              * @param {Roo.bootstrap.ComboBox} combo This combo box
12321              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12322              */
12323         'edit' : true,
12324         /**
12325          * @event remove
12326          * Fires when the remove value from the combobox array
12327              * @param {Roo.bootstrap.ComboBox} combo This combo box
12328              */
12329         'remove' : true,
12330         /**
12331          * @event afterremove
12332          * Fires when the remove value from the combobox array
12333              * @param {Roo.bootstrap.ComboBox} combo This combo box
12334              */
12335         'afterremove' : true,
12336         /**
12337          * @event specialfilter
12338          * Fires when specialfilter
12339             * @param {Roo.bootstrap.ComboBox} combo This combo box
12340             */
12341         'specialfilter' : true,
12342         /**
12343          * @event tick
12344          * Fires when tick the element
12345             * @param {Roo.bootstrap.ComboBox} combo This combo box
12346             */
12347         'tick' : true,
12348         /**
12349          * @event touchviewdisplay
12350          * Fires when touch view require special display (default is using displayField)
12351             * @param {Roo.bootstrap.ComboBox} combo This combo box
12352             * @param {Object} cfg set html .
12353             */
12354         'touchviewdisplay' : true
12355         
12356     });
12357     
12358     this.item = [];
12359     this.tickItems = [];
12360     
12361     this.selectedIndex = -1;
12362     if(this.mode == 'local'){
12363         if(config.queryDelay === undefined){
12364             this.queryDelay = 10;
12365         }
12366         if(config.minChars === undefined){
12367             this.minChars = 0;
12368         }
12369     }
12370 };
12371
12372 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12373      
12374     /**
12375      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12376      * rendering into an Roo.Editor, defaults to false)
12377      */
12378     /**
12379      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12380      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12381      */
12382     /**
12383      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12384      */
12385     /**
12386      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12387      * the dropdown list (defaults to undefined, with no header element)
12388      */
12389
12390      /**
12391      * @cfg {String/Roo.Template} tpl The template to use to render the output
12392      */
12393      
12394      /**
12395      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12396      */
12397     listWidth: undefined,
12398     /**
12399      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12400      * mode = 'remote' or 'text' if mode = 'local')
12401      */
12402     displayField: undefined,
12403     
12404     /**
12405      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12406      * mode = 'remote' or 'value' if mode = 'local'). 
12407      * Note: use of a valueField requires the user make a selection
12408      * in order for a value to be mapped.
12409      */
12410     valueField: undefined,
12411     /**
12412      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12413      */
12414     modalTitle : '',
12415     
12416     /**
12417      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12418      * field's data value (defaults to the underlying DOM element's name)
12419      */
12420     hiddenName: undefined,
12421     /**
12422      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12423      */
12424     listClass: '',
12425     /**
12426      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12427      */
12428     selectedClass: 'active',
12429     
12430     /**
12431      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12432      */
12433     shadow:'sides',
12434     /**
12435      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12436      * anchor positions (defaults to 'tl-bl')
12437      */
12438     listAlign: 'tl-bl?',
12439     /**
12440      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12441      */
12442     maxHeight: 300,
12443     /**
12444      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12445      * query specified by the allQuery config option (defaults to 'query')
12446      */
12447     triggerAction: 'query',
12448     /**
12449      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12450      * (defaults to 4, does not apply if editable = false)
12451      */
12452     minChars : 4,
12453     /**
12454      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12455      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12456      */
12457     typeAhead: false,
12458     /**
12459      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12460      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12461      */
12462     queryDelay: 500,
12463     /**
12464      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12465      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12466      */
12467     pageSize: 0,
12468     /**
12469      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12470      * when editable = true (defaults to false)
12471      */
12472     selectOnFocus:false,
12473     /**
12474      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12475      */
12476     queryParam: 'query',
12477     /**
12478      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12479      * when mode = 'remote' (defaults to 'Loading...')
12480      */
12481     loadingText: 'Loading...',
12482     /**
12483      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12484      */
12485     resizable: false,
12486     /**
12487      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12488      */
12489     handleHeight : 8,
12490     /**
12491      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12492      * traditional select (defaults to true)
12493      */
12494     editable: true,
12495     /**
12496      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12497      */
12498     allQuery: '',
12499     /**
12500      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12501      */
12502     mode: 'remote',
12503     /**
12504      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12505      * listWidth has a higher value)
12506      */
12507     minListWidth : 70,
12508     /**
12509      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12510      * allow the user to set arbitrary text into the field (defaults to false)
12511      */
12512     forceSelection:false,
12513     /**
12514      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12515      * if typeAhead = true (defaults to 250)
12516      */
12517     typeAheadDelay : 250,
12518     /**
12519      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12520      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12521      */
12522     valueNotFoundText : undefined,
12523     /**
12524      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12525      */
12526     blockFocus : false,
12527     
12528     /**
12529      * @cfg {Boolean} disableClear Disable showing of clear button.
12530      */
12531     disableClear : false,
12532     /**
12533      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12534      */
12535     alwaysQuery : false,
12536     
12537     /**
12538      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12539      */
12540     multiple : false,
12541     
12542     /**
12543      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12544      */
12545     invalidClass : "has-warning",
12546     
12547     /**
12548      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12549      */
12550     validClass : "has-success",
12551     
12552     /**
12553      * @cfg {Boolean} specialFilter (true|false) special filter default false
12554      */
12555     specialFilter : false,
12556     
12557     /**
12558      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12559      */
12560     mobileTouchView : true,
12561     
12562     /**
12563      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12564      */
12565     useNativeIOS : false,
12566     
12567     ios_options : false,
12568     
12569     //private
12570     addicon : false,
12571     editicon: false,
12572     
12573     page: 0,
12574     hasQuery: false,
12575     append: false,
12576     loadNext: false,
12577     autoFocus : true,
12578     tickable : false,
12579     btnPosition : 'right',
12580     triggerList : true,
12581     showToggleBtn : true,
12582     animate : true,
12583     emptyResultText: 'Empty',
12584     triggerText : 'Select',
12585     
12586     // element that contains real text value.. (when hidden is used..)
12587     
12588     getAutoCreate : function()
12589     {   
12590         var cfg = false;
12591         //render
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                         cn : [
12777                             {
12778                                 tag : 'i',
12779                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12780                                 tooltip : 'This field is required'
12781                             }
12782                         ]
12783                     },
12784                     
12785                     {
12786                         cls : "",
12787                         cn: [
12788                             combobox
12789                         ]
12790                     }
12791
12792                 ];
12793                 
12794                 labelCfg = cfg.cn[0];
12795                 contentCfg = cfg.cn[2];
12796             
12797             }
12798             
12799             if(this.labelWidth > 12){
12800                 labelCfg.style = "width: " + this.labelWidth + 'px';
12801             }
12802             
12803             if(this.labelWidth < 13 && this.labelmd == 0){
12804                 this.labelmd = this.labelWidth;
12805             }
12806             
12807             if(this.labellg > 0){
12808                 labelCfg.cls += ' col-lg-' + this.labellg;
12809                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12810             }
12811             
12812             if(this.labelmd > 0){
12813                 labelCfg.cls += ' col-md-' + this.labelmd;
12814                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12815             }
12816             
12817             if(this.labelsm > 0){
12818                 labelCfg.cls += ' col-sm-' + this.labelsm;
12819                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12820             }
12821             
12822             if(this.labelxs > 0){
12823                 labelCfg.cls += ' col-xs-' + this.labelxs;
12824                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12825             }
12826                 
12827                 
12828         } else if ( this.fieldLabel.length) {
12829 //                Roo.log(" label");
12830                  cfg.cn = [
12831                     {
12832                         tag : 'i',
12833                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12834                         tooltip : 'This field is required'
12835                     },
12836                     {
12837                         tag: 'label',
12838                         //cls : 'input-group-addon',
12839                         html : this.fieldLabel
12840                         
12841                     },
12842                     
12843                     combobox
12844                     
12845                 ];
12846                 
12847                 if(this.indicatorpos == 'right'){
12848                     
12849                     cfg.cn = [
12850                         {
12851                             tag: 'label',
12852                             //cls : 'input-group-addon',
12853                             html : this.fieldLabel,
12854                             cn : [
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                         },
12862                         
12863                         
12864                         
12865                         combobox
12866
12867                     ];
12868                 
12869                 }
12870
12871         } else {
12872             
12873 //                Roo.log(" no label && no align");
12874                 cfg = combobox
12875                      
12876                 
12877         }
12878          
12879         var settings=this;
12880         ['xs','sm','md','lg'].map(function(size){
12881             if (settings[size]) {
12882                 cfg.cls += ' col-' + size + '-' + settings[size];
12883             }
12884         });
12885         
12886         return cfg;
12887         
12888     },
12889     
12890     _initEventsCalled : false,
12891     
12892     // private
12893     initEvents: function()
12894     {   
12895         if (this._initEventsCalled) { // as we call render... prevent looping...
12896             return;
12897         }
12898         this._initEventsCalled = true;
12899         
12900         if (!this.store) {
12901             throw "can not find store for combo";
12902         }
12903         
12904         this.store = Roo.factory(this.store, Roo.data);
12905         
12906         // if we are building from html. then this element is so complex, that we can not really
12907         // use the rendered HTML.
12908         // so we have to trash and replace the previous code.
12909         if (Roo.XComponent.build_from_html) {
12910             
12911             // remove this element....
12912             var e = this.el.dom, k=0;
12913             while (e ) { e = e.previousSibling;  ++k;}
12914
12915             this.el.remove();
12916             
12917             this.el=false;
12918             this.rendered = false;
12919             
12920             this.render(this.parent().getChildContainer(true), k);
12921             
12922             
12923             
12924         }
12925         
12926         if(Roo.isIOS && this.useNativeIOS){
12927             this.initIOSView();
12928             return;
12929         }
12930         
12931         /*
12932          * Touch Devices
12933          */
12934         
12935         if(Roo.isTouch && this.mobileTouchView){
12936             this.initTouchView();
12937             return;
12938         }
12939         
12940         if(this.tickable){
12941             this.initTickableEvents();
12942             return;
12943         }
12944         
12945         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12946         
12947         if(this.hiddenName){
12948             
12949             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12950             
12951             this.hiddenField.dom.value =
12952                 this.hiddenValue !== undefined ? this.hiddenValue :
12953                 this.value !== undefined ? this.value : '';
12954
12955             // prevent input submission
12956             this.el.dom.removeAttribute('name');
12957             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12958              
12959              
12960         }
12961         //if(Roo.isGecko){
12962         //    this.el.dom.setAttribute('autocomplete', 'off');
12963         //}
12964         
12965         var cls = 'x-combo-list';
12966         
12967         //this.list = new Roo.Layer({
12968         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12969         //});
12970         
12971         var _this = this;
12972         
12973         (function(){
12974             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12975             _this.list.setWidth(lw);
12976         }).defer(100);
12977         
12978         this.list.on('mouseover', this.onViewOver, this);
12979         this.list.on('mousemove', this.onViewMove, this);
12980         
12981         this.list.on('scroll', this.onViewScroll, this);
12982         
12983         /*
12984         this.list.swallowEvent('mousewheel');
12985         this.assetHeight = 0;
12986
12987         if(this.title){
12988             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12989             this.assetHeight += this.header.getHeight();
12990         }
12991
12992         this.innerList = this.list.createChild({cls:cls+'-inner'});
12993         this.innerList.on('mouseover', this.onViewOver, this);
12994         this.innerList.on('mousemove', this.onViewMove, this);
12995         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12996         
12997         if(this.allowBlank && !this.pageSize && !this.disableClear){
12998             this.footer = this.list.createChild({cls:cls+'-ft'});
12999             this.pageTb = new Roo.Toolbar(this.footer);
13000            
13001         }
13002         if(this.pageSize){
13003             this.footer = this.list.createChild({cls:cls+'-ft'});
13004             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13005                     {pageSize: this.pageSize});
13006             
13007         }
13008         
13009         if (this.pageTb && this.allowBlank && !this.disableClear) {
13010             var _this = this;
13011             this.pageTb.add(new Roo.Toolbar.Fill(), {
13012                 cls: 'x-btn-icon x-btn-clear',
13013                 text: '&#160;',
13014                 handler: function()
13015                 {
13016                     _this.collapse();
13017                     _this.clearValue();
13018                     _this.onSelect(false, -1);
13019                 }
13020             });
13021         }
13022         if (this.footer) {
13023             this.assetHeight += this.footer.getHeight();
13024         }
13025         */
13026             
13027         if(!this.tpl){
13028             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13029         }
13030
13031         this.view = new Roo.View(this.list, this.tpl, {
13032             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13033         });
13034         //this.view.wrapEl.setDisplayed(false);
13035         this.view.on('click', this.onViewClick, this);
13036         
13037         
13038         
13039         this.store.on('beforeload', this.onBeforeLoad, this);
13040         this.store.on('load', this.onLoad, this);
13041         this.store.on('loadexception', this.onLoadException, this);
13042         /*
13043         if(this.resizable){
13044             this.resizer = new Roo.Resizable(this.list,  {
13045                pinned:true, handles:'se'
13046             });
13047             this.resizer.on('resize', function(r, w, h){
13048                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13049                 this.listWidth = w;
13050                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13051                 this.restrictHeight();
13052             }, this);
13053             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13054         }
13055         */
13056         if(!this.editable){
13057             this.editable = true;
13058             this.setEditable(false);
13059         }
13060         
13061         /*
13062         
13063         if (typeof(this.events.add.listeners) != 'undefined') {
13064             
13065             this.addicon = this.wrap.createChild(
13066                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13067        
13068             this.addicon.on('click', function(e) {
13069                 this.fireEvent('add', this);
13070             }, this);
13071         }
13072         if (typeof(this.events.edit.listeners) != 'undefined') {
13073             
13074             this.editicon = this.wrap.createChild(
13075                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13076             if (this.addicon) {
13077                 this.editicon.setStyle('margin-left', '40px');
13078             }
13079             this.editicon.on('click', function(e) {
13080                 
13081                 // we fire even  if inothing is selected..
13082                 this.fireEvent('edit', this, this.lastData );
13083                 
13084             }, this);
13085         }
13086         */
13087         
13088         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13089             "up" : function(e){
13090                 this.inKeyMode = true;
13091                 this.selectPrev();
13092             },
13093
13094             "down" : function(e){
13095                 if(!this.isExpanded()){
13096                     this.onTriggerClick();
13097                 }else{
13098                     this.inKeyMode = true;
13099                     this.selectNext();
13100                 }
13101             },
13102
13103             "enter" : function(e){
13104 //                this.onViewClick();
13105                 //return true;
13106                 this.collapse();
13107                 
13108                 if(this.fireEvent("specialkey", this, e)){
13109                     this.onViewClick(false);
13110                 }
13111                 
13112                 return true;
13113             },
13114
13115             "esc" : function(e){
13116                 this.collapse();
13117             },
13118
13119             "tab" : function(e){
13120                 this.collapse();
13121                 
13122                 if(this.fireEvent("specialkey", this, e)){
13123                     this.onViewClick(false);
13124                 }
13125                 
13126                 return true;
13127             },
13128
13129             scope : this,
13130
13131             doRelay : function(foo, bar, hname){
13132                 if(hname == 'down' || this.scope.isExpanded()){
13133                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13134                 }
13135                 return true;
13136             },
13137
13138             forceKeyDown: true
13139         });
13140         
13141         
13142         this.queryDelay = Math.max(this.queryDelay || 10,
13143                 this.mode == 'local' ? 10 : 250);
13144         
13145         
13146         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13147         
13148         if(this.typeAhead){
13149             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13150         }
13151         if(this.editable !== false){
13152             this.inputEl().on("keyup", this.onKeyUp, this);
13153         }
13154         if(this.forceSelection){
13155             this.inputEl().on('blur', this.doForce, this);
13156         }
13157         
13158         if(this.multiple){
13159             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13160             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13161         }
13162     },
13163     
13164     initTickableEvents: function()
13165     {   
13166         this.createList();
13167         
13168         if(this.hiddenName){
13169             
13170             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13171             
13172             this.hiddenField.dom.value =
13173                 this.hiddenValue !== undefined ? this.hiddenValue :
13174                 this.value !== undefined ? this.value : '';
13175
13176             // prevent input submission
13177             this.el.dom.removeAttribute('name');
13178             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13179              
13180              
13181         }
13182         
13183 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13184         
13185         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13186         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13187         if(this.triggerList){
13188             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13189         }
13190          
13191         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13192         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13193         
13194         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13195         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13196         
13197         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13198         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13199         
13200         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13201         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13202         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13203         
13204         this.okBtn.hide();
13205         this.cancelBtn.hide();
13206         
13207         var _this = this;
13208         
13209         (function(){
13210             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13211             _this.list.setWidth(lw);
13212         }).defer(100);
13213         
13214         this.list.on('mouseover', this.onViewOver, this);
13215         this.list.on('mousemove', this.onViewMove, this);
13216         
13217         this.list.on('scroll', this.onViewScroll, this);
13218         
13219         if(!this.tpl){
13220             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>';
13221         }
13222
13223         this.view = new Roo.View(this.list, this.tpl, {
13224             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13225         });
13226         
13227         //this.view.wrapEl.setDisplayed(false);
13228         this.view.on('click', this.onViewClick, this);
13229         
13230         
13231         
13232         this.store.on('beforeload', this.onBeforeLoad, this);
13233         this.store.on('load', this.onLoad, this);
13234         this.store.on('loadexception', this.onLoadException, this);
13235         
13236         if(this.editable){
13237             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13238                 "up" : function(e){
13239                     this.inKeyMode = true;
13240                     this.selectPrev();
13241                 },
13242
13243                 "down" : function(e){
13244                     this.inKeyMode = true;
13245                     this.selectNext();
13246                 },
13247
13248                 "enter" : function(e){
13249                     if(this.fireEvent("specialkey", this, e)){
13250                         this.onViewClick(false);
13251                     }
13252                     
13253                     return true;
13254                 },
13255
13256                 "esc" : function(e){
13257                     this.onTickableFooterButtonClick(e, false, false);
13258                 },
13259
13260                 "tab" : function(e){
13261                     this.fireEvent("specialkey", this, e);
13262                     
13263                     this.onTickableFooterButtonClick(e, false, false);
13264                     
13265                     return true;
13266                 },
13267
13268                 scope : this,
13269
13270                 doRelay : function(e, fn, key){
13271                     if(this.scope.isExpanded()){
13272                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13273                     }
13274                     return true;
13275                 },
13276
13277                 forceKeyDown: true
13278             });
13279         }
13280         
13281         this.queryDelay = Math.max(this.queryDelay || 10,
13282                 this.mode == 'local' ? 10 : 250);
13283         
13284         
13285         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13286         
13287         if(this.typeAhead){
13288             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13289         }
13290         
13291         if(this.editable !== false){
13292             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13293         }
13294         
13295         this.indicator = this.indicatorEl();
13296         
13297         if(this.indicator){
13298             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13299             this.indicator.hide();
13300         }
13301         
13302     },
13303
13304     onDestroy : function(){
13305         if(this.view){
13306             this.view.setStore(null);
13307             this.view.el.removeAllListeners();
13308             this.view.el.remove();
13309             this.view.purgeListeners();
13310         }
13311         if(this.list){
13312             this.list.dom.innerHTML  = '';
13313         }
13314         
13315         if(this.store){
13316             this.store.un('beforeload', this.onBeforeLoad, this);
13317             this.store.un('load', this.onLoad, this);
13318             this.store.un('loadexception', this.onLoadException, this);
13319         }
13320         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13321     },
13322
13323     // private
13324     fireKey : function(e){
13325         if(e.isNavKeyPress() && !this.list.isVisible()){
13326             this.fireEvent("specialkey", this, e);
13327         }
13328     },
13329
13330     // private
13331     onResize: function(w, h){
13332 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13333 //        
13334 //        if(typeof w != 'number'){
13335 //            // we do not handle it!?!?
13336 //            return;
13337 //        }
13338 //        var tw = this.trigger.getWidth();
13339 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13340 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13341 //        var x = w - tw;
13342 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13343 //            
13344 //        //this.trigger.setStyle('left', x+'px');
13345 //        
13346 //        if(this.list && this.listWidth === undefined){
13347 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13348 //            this.list.setWidth(lw);
13349 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13350 //        }
13351         
13352     
13353         
13354     },
13355
13356     /**
13357      * Allow or prevent the user from directly editing the field text.  If false is passed,
13358      * the user will only be able to select from the items defined in the dropdown list.  This method
13359      * is the runtime equivalent of setting the 'editable' config option at config time.
13360      * @param {Boolean} value True to allow the user to directly edit the field text
13361      */
13362     setEditable : function(value){
13363         if(value == this.editable){
13364             return;
13365         }
13366         this.editable = value;
13367         if(!value){
13368             this.inputEl().dom.setAttribute('readOnly', true);
13369             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13370             this.inputEl().addClass('x-combo-noedit');
13371         }else{
13372             this.inputEl().dom.setAttribute('readOnly', false);
13373             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13374             this.inputEl().removeClass('x-combo-noedit');
13375         }
13376     },
13377
13378     // private
13379     
13380     onBeforeLoad : function(combo,opts){
13381         if(!this.hasFocus){
13382             return;
13383         }
13384          if (!opts.add) {
13385             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13386          }
13387         this.restrictHeight();
13388         this.selectedIndex = -1;
13389     },
13390
13391     // private
13392     onLoad : function(){
13393         
13394         this.hasQuery = false;
13395         
13396         if(!this.hasFocus){
13397             return;
13398         }
13399         
13400         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13401             this.loading.hide();
13402         }
13403              
13404         if(this.store.getCount() > 0){
13405             this.expand();
13406             this.restrictHeight();
13407             if(this.lastQuery == this.allQuery){
13408                 if(this.editable && !this.tickable){
13409                     this.inputEl().dom.select();
13410                 }
13411                 
13412                 if(
13413                     !this.selectByValue(this.value, true) &&
13414                     this.autoFocus && 
13415                     (
13416                         !this.store.lastOptions ||
13417                         typeof(this.store.lastOptions.add) == 'undefined' || 
13418                         this.store.lastOptions.add != true
13419                     )
13420                 ){
13421                     this.select(0, true);
13422                 }
13423             }else{
13424                 if(this.autoFocus){
13425                     this.selectNext();
13426                 }
13427                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13428                     this.taTask.delay(this.typeAheadDelay);
13429                 }
13430             }
13431         }else{
13432             this.onEmptyResults();
13433         }
13434         
13435         //this.el.focus();
13436     },
13437     // private
13438     onLoadException : function()
13439     {
13440         this.hasQuery = false;
13441         
13442         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13443             this.loading.hide();
13444         }
13445         
13446         if(this.tickable && this.editable){
13447             return;
13448         }
13449         
13450         this.collapse();
13451         // only causes errors at present
13452         //Roo.log(this.store.reader.jsonData);
13453         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13454             // fixme
13455             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13456         //}
13457         
13458         
13459     },
13460     // private
13461     onTypeAhead : function(){
13462         if(this.store.getCount() > 0){
13463             var r = this.store.getAt(0);
13464             var newValue = r.data[this.displayField];
13465             var len = newValue.length;
13466             var selStart = this.getRawValue().length;
13467             
13468             if(selStart != len){
13469                 this.setRawValue(newValue);
13470                 this.selectText(selStart, newValue.length);
13471             }
13472         }
13473     },
13474
13475     // private
13476     onSelect : function(record, index){
13477         
13478         if(this.fireEvent('beforeselect', this, record, index) !== false){
13479         
13480             this.setFromData(index > -1 ? record.data : false);
13481             
13482             this.collapse();
13483             this.fireEvent('select', this, record, index);
13484         }
13485     },
13486
13487     /**
13488      * Returns the currently selected field value or empty string if no value is set.
13489      * @return {String} value The selected value
13490      */
13491     getValue : function()
13492     {
13493         if(Roo.isIOS && this.useNativeIOS){
13494             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13495         }
13496         
13497         if(this.multiple){
13498             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13499         }
13500         
13501         if(this.valueField){
13502             return typeof this.value != 'undefined' ? this.value : '';
13503         }else{
13504             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13505         }
13506     },
13507     
13508     getRawValue : function()
13509     {
13510         if(Roo.isIOS && this.useNativeIOS){
13511             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13512         }
13513         
13514         var v = this.inputEl().getValue();
13515         
13516         return v;
13517     },
13518
13519     /**
13520      * Clears any text/value currently set in the field
13521      */
13522     clearValue : function(){
13523         
13524         if(this.hiddenField){
13525             this.hiddenField.dom.value = '';
13526         }
13527         this.value = '';
13528         this.setRawValue('');
13529         this.lastSelectionText = '';
13530         this.lastData = false;
13531         
13532         var close = this.closeTriggerEl();
13533         
13534         if(close){
13535             close.hide();
13536         }
13537         
13538         this.validate();
13539         
13540     },
13541
13542     /**
13543      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13544      * will be displayed in the field.  If the value does not match the data value of an existing item,
13545      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13546      * Otherwise the field will be blank (although the value will still be set).
13547      * @param {String} value The value to match
13548      */
13549     setValue : function(v)
13550     {
13551         if(Roo.isIOS && this.useNativeIOS){
13552             this.setIOSValue(v);
13553             return;
13554         }
13555         
13556         if(this.multiple){
13557             this.syncValue();
13558             return;
13559         }
13560         
13561         var text = v;
13562         if(this.valueField){
13563             var r = this.findRecord(this.valueField, v);
13564             if(r){
13565                 text = r.data[this.displayField];
13566             }else if(this.valueNotFoundText !== undefined){
13567                 text = this.valueNotFoundText;
13568             }
13569         }
13570         this.lastSelectionText = text;
13571         if(this.hiddenField){
13572             this.hiddenField.dom.value = v;
13573         }
13574         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13575         this.value = v;
13576         
13577         var close = this.closeTriggerEl();
13578         
13579         if(close){
13580             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13581         }
13582         
13583         this.validate();
13584     },
13585     /**
13586      * @property {Object} the last set data for the element
13587      */
13588     
13589     lastData : false,
13590     /**
13591      * Sets the value of the field based on a object which is related to the record format for the store.
13592      * @param {Object} value the value to set as. or false on reset?
13593      */
13594     setFromData : function(o){
13595         
13596         if(this.multiple){
13597             this.addItem(o);
13598             return;
13599         }
13600             
13601         var dv = ''; // display value
13602         var vv = ''; // value value..
13603         this.lastData = o;
13604         if (this.displayField) {
13605             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13606         } else {
13607             // this is an error condition!!!
13608             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13609         }
13610         
13611         if(this.valueField){
13612             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13613         }
13614         
13615         var close = this.closeTriggerEl();
13616         
13617         if(close){
13618             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13619         }
13620         
13621         if(this.hiddenField){
13622             this.hiddenField.dom.value = vv;
13623             
13624             this.lastSelectionText = dv;
13625             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13626             this.value = vv;
13627             return;
13628         }
13629         // no hidden field.. - we store the value in 'value', but still display
13630         // display field!!!!
13631         this.lastSelectionText = dv;
13632         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13633         this.value = vv;
13634         
13635         
13636         
13637     },
13638     // private
13639     reset : function(){
13640         // overridden so that last data is reset..
13641         
13642         if(this.multiple){
13643             this.clearItem();
13644             return;
13645         }
13646         
13647         this.setValue(this.originalValue);
13648         //this.clearInvalid();
13649         this.lastData = false;
13650         if (this.view) {
13651             this.view.clearSelections();
13652         }
13653         
13654         this.validate();
13655     },
13656     // private
13657     findRecord : function(prop, value){
13658         var record;
13659         if(this.store.getCount() > 0){
13660             this.store.each(function(r){
13661                 if(r.data[prop] == value){
13662                     record = r;
13663                     return false;
13664                 }
13665                 return true;
13666             });
13667         }
13668         return record;
13669     },
13670     
13671     getName: function()
13672     {
13673         // returns hidden if it's set..
13674         if (!this.rendered) {return ''};
13675         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13676         
13677     },
13678     // private
13679     onViewMove : function(e, t){
13680         this.inKeyMode = false;
13681     },
13682
13683     // private
13684     onViewOver : function(e, t){
13685         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13686             return;
13687         }
13688         var item = this.view.findItemFromChild(t);
13689         
13690         if(item){
13691             var index = this.view.indexOf(item);
13692             this.select(index, false);
13693         }
13694     },
13695
13696     // private
13697     onViewClick : function(view, doFocus, el, e)
13698     {
13699         var index = this.view.getSelectedIndexes()[0];
13700         
13701         var r = this.store.getAt(index);
13702         
13703         if(this.tickable){
13704             
13705             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13706                 return;
13707             }
13708             
13709             var rm = false;
13710             var _this = this;
13711             
13712             Roo.each(this.tickItems, function(v,k){
13713                 
13714                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13715                     Roo.log(v);
13716                     _this.tickItems.splice(k, 1);
13717                     
13718                     if(typeof(e) == 'undefined' && view == false){
13719                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13720                     }
13721                     
13722                     rm = true;
13723                     return;
13724                 }
13725             });
13726             
13727             if(rm){
13728                 return;
13729             }
13730             
13731             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13732                 this.tickItems.push(r.data);
13733             }
13734             
13735             if(typeof(e) == 'undefined' && view == false){
13736                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13737             }
13738                     
13739             return;
13740         }
13741         
13742         if(r){
13743             this.onSelect(r, index);
13744         }
13745         if(doFocus !== false && !this.blockFocus){
13746             this.inputEl().focus();
13747         }
13748     },
13749
13750     // private
13751     restrictHeight : function(){
13752         //this.innerList.dom.style.height = '';
13753         //var inner = this.innerList.dom;
13754         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13755         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13756         //this.list.beginUpdate();
13757         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13758         this.list.alignTo(this.inputEl(), this.listAlign);
13759         this.list.alignTo(this.inputEl(), this.listAlign);
13760         //this.list.endUpdate();
13761     },
13762
13763     // private
13764     onEmptyResults : function(){
13765         
13766         if(this.tickable && this.editable){
13767             this.restrictHeight();
13768             return;
13769         }
13770         
13771         this.collapse();
13772     },
13773
13774     /**
13775      * Returns true if the dropdown list is expanded, else false.
13776      */
13777     isExpanded : function(){
13778         return this.list.isVisible();
13779     },
13780
13781     /**
13782      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13783      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13784      * @param {String} value The data value of the item to select
13785      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13786      * selected item if it is not currently in view (defaults to true)
13787      * @return {Boolean} True if the value matched an item in the list, else false
13788      */
13789     selectByValue : function(v, scrollIntoView){
13790         if(v !== undefined && v !== null){
13791             var r = this.findRecord(this.valueField || this.displayField, v);
13792             if(r){
13793                 this.select(this.store.indexOf(r), scrollIntoView);
13794                 return true;
13795             }
13796         }
13797         return false;
13798     },
13799
13800     /**
13801      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13802      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13803      * @param {Number} index The zero-based index of the list item to select
13804      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13805      * selected item if it is not currently in view (defaults to true)
13806      */
13807     select : function(index, scrollIntoView){
13808         this.selectedIndex = index;
13809         this.view.select(index);
13810         if(scrollIntoView !== false){
13811             var el = this.view.getNode(index);
13812             /*
13813              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13814              */
13815             if(el){
13816                 this.list.scrollChildIntoView(el, false);
13817             }
13818         }
13819     },
13820
13821     // private
13822     selectNext : function(){
13823         var ct = this.store.getCount();
13824         if(ct > 0){
13825             if(this.selectedIndex == -1){
13826                 this.select(0);
13827             }else if(this.selectedIndex < ct-1){
13828                 this.select(this.selectedIndex+1);
13829             }
13830         }
13831     },
13832
13833     // private
13834     selectPrev : function(){
13835         var ct = this.store.getCount();
13836         if(ct > 0){
13837             if(this.selectedIndex == -1){
13838                 this.select(0);
13839             }else if(this.selectedIndex != 0){
13840                 this.select(this.selectedIndex-1);
13841             }
13842         }
13843     },
13844
13845     // private
13846     onKeyUp : function(e){
13847         if(this.editable !== false && !e.isSpecialKey()){
13848             this.lastKey = e.getKey();
13849             this.dqTask.delay(this.queryDelay);
13850         }
13851     },
13852
13853     // private
13854     validateBlur : function(){
13855         return !this.list || !this.list.isVisible();   
13856     },
13857
13858     // private
13859     initQuery : function(){
13860         
13861         var v = this.getRawValue();
13862         
13863         if(this.tickable && this.editable){
13864             v = this.tickableInputEl().getValue();
13865         }
13866         
13867         this.doQuery(v);
13868     },
13869
13870     // private
13871     doForce : function(){
13872         if(this.inputEl().dom.value.length > 0){
13873             this.inputEl().dom.value =
13874                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13875              
13876         }
13877     },
13878
13879     /**
13880      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13881      * query allowing the query action to be canceled if needed.
13882      * @param {String} query The SQL query to execute
13883      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13884      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13885      * saved in the current store (defaults to false)
13886      */
13887     doQuery : function(q, forceAll){
13888         
13889         if(q === undefined || q === null){
13890             q = '';
13891         }
13892         var qe = {
13893             query: q,
13894             forceAll: forceAll,
13895             combo: this,
13896             cancel:false
13897         };
13898         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13899             return false;
13900         }
13901         q = qe.query;
13902         
13903         forceAll = qe.forceAll;
13904         if(forceAll === true || (q.length >= this.minChars)){
13905             
13906             this.hasQuery = true;
13907             
13908             if(this.lastQuery != q || this.alwaysQuery){
13909                 this.lastQuery = q;
13910                 if(this.mode == 'local'){
13911                     this.selectedIndex = -1;
13912                     if(forceAll){
13913                         this.store.clearFilter();
13914                     }else{
13915                         
13916                         if(this.specialFilter){
13917                             this.fireEvent('specialfilter', this);
13918                             this.onLoad();
13919                             return;
13920                         }
13921                         
13922                         this.store.filter(this.displayField, q);
13923                     }
13924                     
13925                     this.store.fireEvent("datachanged", this.store);
13926                     
13927                     this.onLoad();
13928                     
13929                     
13930                 }else{
13931                     
13932                     this.store.baseParams[this.queryParam] = q;
13933                     
13934                     var options = {params : this.getParams(q)};
13935                     
13936                     if(this.loadNext){
13937                         options.add = true;
13938                         options.params.start = this.page * this.pageSize;
13939                     }
13940                     
13941                     this.store.load(options);
13942                     
13943                     /*
13944                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13945                      *  we should expand the list on onLoad
13946                      *  so command out it
13947                      */
13948 //                    this.expand();
13949                 }
13950             }else{
13951                 this.selectedIndex = -1;
13952                 this.onLoad();   
13953             }
13954         }
13955         
13956         this.loadNext = false;
13957     },
13958     
13959     // private
13960     getParams : function(q){
13961         var p = {};
13962         //p[this.queryParam] = q;
13963         
13964         if(this.pageSize){
13965             p.start = 0;
13966             p.limit = this.pageSize;
13967         }
13968         return p;
13969     },
13970
13971     /**
13972      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13973      */
13974     collapse : function(){
13975         if(!this.isExpanded()){
13976             return;
13977         }
13978         
13979         this.list.hide();
13980         
13981         this.hasFocus = false;
13982         
13983         if(this.tickable){
13984             this.okBtn.hide();
13985             this.cancelBtn.hide();
13986             this.trigger.show();
13987             
13988             if(this.editable){
13989                 this.tickableInputEl().dom.value = '';
13990                 this.tickableInputEl().blur();
13991             }
13992             
13993         }
13994         
13995         Roo.get(document).un('mousedown', this.collapseIf, this);
13996         Roo.get(document).un('mousewheel', this.collapseIf, this);
13997         if (!this.editable) {
13998             Roo.get(document).un('keydown', this.listKeyPress, this);
13999         }
14000         this.fireEvent('collapse', this);
14001         
14002         this.validate();
14003     },
14004
14005     // private
14006     collapseIf : function(e){
14007         var in_combo  = e.within(this.el);
14008         var in_list =  e.within(this.list);
14009         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14010         
14011         if (in_combo || in_list || is_list) {
14012             //e.stopPropagation();
14013             return;
14014         }
14015         
14016         if(this.tickable){
14017             this.onTickableFooterButtonClick(e, false, false);
14018         }
14019
14020         this.collapse();
14021         
14022     },
14023
14024     /**
14025      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14026      */
14027     expand : function(){
14028        
14029         if(this.isExpanded() || !this.hasFocus){
14030             return;
14031         }
14032         
14033         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14034         this.list.setWidth(lw);
14035         
14036         Roo.log('expand');
14037         
14038         this.list.show();
14039         
14040         this.restrictHeight();
14041         
14042         if(this.tickable){
14043             
14044             this.tickItems = Roo.apply([], this.item);
14045             
14046             this.okBtn.show();
14047             this.cancelBtn.show();
14048             this.trigger.hide();
14049             
14050             if(this.editable){
14051                 this.tickableInputEl().focus();
14052             }
14053             
14054         }
14055         
14056         Roo.get(document).on('mousedown', this.collapseIf, this);
14057         Roo.get(document).on('mousewheel', this.collapseIf, this);
14058         if (!this.editable) {
14059             Roo.get(document).on('keydown', this.listKeyPress, this);
14060         }
14061         
14062         this.fireEvent('expand', this);
14063     },
14064
14065     // private
14066     // Implements the default empty TriggerField.onTriggerClick function
14067     onTriggerClick : function(e)
14068     {
14069         Roo.log('trigger click');
14070         
14071         if(this.disabled || !this.triggerList){
14072             return;
14073         }
14074         
14075         this.page = 0;
14076         this.loadNext = false;
14077         
14078         if(this.isExpanded()){
14079             this.collapse();
14080             if (!this.blockFocus) {
14081                 this.inputEl().focus();
14082             }
14083             
14084         }else {
14085             this.hasFocus = true;
14086             if(this.triggerAction == 'all') {
14087                 this.doQuery(this.allQuery, true);
14088             } else {
14089                 this.doQuery(this.getRawValue());
14090             }
14091             if (!this.blockFocus) {
14092                 this.inputEl().focus();
14093             }
14094         }
14095     },
14096     
14097     onTickableTriggerClick : function(e)
14098     {
14099         if(this.disabled){
14100             return;
14101         }
14102         
14103         this.page = 0;
14104         this.loadNext = false;
14105         this.hasFocus = true;
14106         
14107         if(this.triggerAction == 'all') {
14108             this.doQuery(this.allQuery, true);
14109         } else {
14110             this.doQuery(this.getRawValue());
14111         }
14112     },
14113     
14114     onSearchFieldClick : function(e)
14115     {
14116         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14117             this.onTickableFooterButtonClick(e, false, false);
14118             return;
14119         }
14120         
14121         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14122             return;
14123         }
14124         
14125         this.page = 0;
14126         this.loadNext = false;
14127         this.hasFocus = true;
14128         
14129         if(this.triggerAction == 'all') {
14130             this.doQuery(this.allQuery, true);
14131         } else {
14132             this.doQuery(this.getRawValue());
14133         }
14134     },
14135     
14136     listKeyPress : function(e)
14137     {
14138         //Roo.log('listkeypress');
14139         // scroll to first matching element based on key pres..
14140         if (e.isSpecialKey()) {
14141             return false;
14142         }
14143         var k = String.fromCharCode(e.getKey()).toUpperCase();
14144         //Roo.log(k);
14145         var match  = false;
14146         var csel = this.view.getSelectedNodes();
14147         var cselitem = false;
14148         if (csel.length) {
14149             var ix = this.view.indexOf(csel[0]);
14150             cselitem  = this.store.getAt(ix);
14151             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14152                 cselitem = false;
14153             }
14154             
14155         }
14156         
14157         this.store.each(function(v) { 
14158             if (cselitem) {
14159                 // start at existing selection.
14160                 if (cselitem.id == v.id) {
14161                     cselitem = false;
14162                 }
14163                 return true;
14164             }
14165                 
14166             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14167                 match = this.store.indexOf(v);
14168                 return false;
14169             }
14170             return true;
14171         }, this);
14172         
14173         if (match === false) {
14174             return true; // no more action?
14175         }
14176         // scroll to?
14177         this.view.select(match);
14178         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14179         sn.scrollIntoView(sn.dom.parentNode, false);
14180     },
14181     
14182     onViewScroll : function(e, t){
14183         
14184         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){
14185             return;
14186         }
14187         
14188         this.hasQuery = true;
14189         
14190         this.loading = this.list.select('.loading', true).first();
14191         
14192         if(this.loading === null){
14193             this.list.createChild({
14194                 tag: 'div',
14195                 cls: 'loading roo-select2-more-results roo-select2-active',
14196                 html: 'Loading more results...'
14197             });
14198             
14199             this.loading = this.list.select('.loading', true).first();
14200             
14201             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14202             
14203             this.loading.hide();
14204         }
14205         
14206         this.loading.show();
14207         
14208         var _combo = this;
14209         
14210         this.page++;
14211         this.loadNext = true;
14212         
14213         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14214         
14215         return;
14216     },
14217     
14218     addItem : function(o)
14219     {   
14220         var dv = ''; // display value
14221         
14222         if (this.displayField) {
14223             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14224         } else {
14225             // this is an error condition!!!
14226             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14227         }
14228         
14229         if(!dv.length){
14230             return;
14231         }
14232         
14233         var choice = this.choices.createChild({
14234             tag: 'li',
14235             cls: 'roo-select2-search-choice',
14236             cn: [
14237                 {
14238                     tag: 'div',
14239                     html: dv
14240                 },
14241                 {
14242                     tag: 'a',
14243                     href: '#',
14244                     cls: 'roo-select2-search-choice-close',
14245                     tabindex: '-1'
14246                 }
14247             ]
14248             
14249         }, this.searchField);
14250         
14251         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14252         
14253         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14254         
14255         this.item.push(o);
14256         
14257         this.lastData = o;
14258         
14259         this.syncValue();
14260         
14261         this.inputEl().dom.value = '';
14262         
14263         this.validate();
14264     },
14265     
14266     onRemoveItem : function(e, _self, o)
14267     {
14268         e.preventDefault();
14269         
14270         this.lastItem = Roo.apply([], this.item);
14271         
14272         var index = this.item.indexOf(o.data) * 1;
14273         
14274         if( index < 0){
14275             Roo.log('not this item?!');
14276             return;
14277         }
14278         
14279         this.item.splice(index, 1);
14280         o.item.remove();
14281         
14282         this.syncValue();
14283         
14284         this.fireEvent('remove', this, e);
14285         
14286         this.validate();
14287         
14288     },
14289     
14290     syncValue : function()
14291     {
14292         if(!this.item.length){
14293             this.clearValue();
14294             return;
14295         }
14296             
14297         var value = [];
14298         var _this = this;
14299         Roo.each(this.item, function(i){
14300             if(_this.valueField){
14301                 value.push(i[_this.valueField]);
14302                 return;
14303             }
14304
14305             value.push(i);
14306         });
14307
14308         this.value = value.join(',');
14309
14310         if(this.hiddenField){
14311             this.hiddenField.dom.value = this.value;
14312         }
14313         
14314         this.store.fireEvent("datachanged", this.store);
14315         
14316         this.validate();
14317     },
14318     
14319     clearItem : function()
14320     {
14321         if(!this.multiple){
14322             return;
14323         }
14324         
14325         this.item = [];
14326         
14327         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14328            c.remove();
14329         });
14330         
14331         this.syncValue();
14332         
14333         this.validate();
14334         
14335         if(this.tickable && !Roo.isTouch){
14336             this.view.refresh();
14337         }
14338     },
14339     
14340     inputEl: function ()
14341     {
14342         if(Roo.isIOS && this.useNativeIOS){
14343             return this.el.select('select.roo-ios-select', true).first();
14344         }
14345         
14346         if(Roo.isTouch && this.mobileTouchView){
14347             return this.el.select('input.form-control',true).first();
14348         }
14349         
14350         if(this.tickable){
14351             return this.searchField;
14352         }
14353         
14354         return this.el.select('input.form-control',true).first();
14355     },
14356     
14357     onTickableFooterButtonClick : function(e, btn, el)
14358     {
14359         e.preventDefault();
14360         
14361         this.lastItem = Roo.apply([], this.item);
14362         
14363         if(btn && btn.name == 'cancel'){
14364             this.tickItems = Roo.apply([], this.item);
14365             this.collapse();
14366             return;
14367         }
14368         
14369         this.clearItem();
14370         
14371         var _this = this;
14372         
14373         Roo.each(this.tickItems, function(o){
14374             _this.addItem(o);
14375         });
14376         
14377         this.collapse();
14378         
14379     },
14380     
14381     validate : function()
14382     {
14383         var v = this.getRawValue();
14384         
14385         if(this.multiple){
14386             v = this.getValue();
14387         }
14388         
14389         if(this.disabled || this.allowBlank || v.length){
14390             this.markValid();
14391             return true;
14392         }
14393         
14394         this.markInvalid();
14395         return false;
14396     },
14397     
14398     tickableInputEl : function()
14399     {
14400         if(!this.tickable || !this.editable){
14401             return this.inputEl();
14402         }
14403         
14404         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14405     },
14406     
14407     
14408     getAutoCreateTouchView : function()
14409     {
14410         var id = Roo.id();
14411         
14412         var cfg = {
14413             cls: 'form-group' //input-group
14414         };
14415         
14416         var input =  {
14417             tag: 'input',
14418             id : id,
14419             type : this.inputType,
14420             cls : 'form-control x-combo-noedit',
14421             autocomplete: 'new-password',
14422             placeholder : this.placeholder || '',
14423             readonly : true
14424         };
14425         
14426         if (this.name) {
14427             input.name = this.name;
14428         }
14429         
14430         if (this.size) {
14431             input.cls += ' input-' + this.size;
14432         }
14433         
14434         if (this.disabled) {
14435             input.disabled = true;
14436         }
14437         
14438         var inputblock = {
14439             cls : '',
14440             cn : [
14441                 input
14442             ]
14443         };
14444         
14445         if(this.before){
14446             inputblock.cls += ' input-group';
14447             
14448             inputblock.cn.unshift({
14449                 tag :'span',
14450                 cls : 'input-group-addon',
14451                 html : this.before
14452             });
14453         }
14454         
14455         if(this.removable && !this.multiple){
14456             inputblock.cls += ' roo-removable';
14457             
14458             inputblock.cn.push({
14459                 tag: 'button',
14460                 html : 'x',
14461                 cls : 'roo-combo-removable-btn close'
14462             });
14463         }
14464
14465         if(this.hasFeedback && !this.allowBlank){
14466             
14467             inputblock.cls += ' has-feedback';
14468             
14469             inputblock.cn.push({
14470                 tag: 'span',
14471                 cls: 'glyphicon form-control-feedback'
14472             });
14473             
14474         }
14475         
14476         if (this.after) {
14477             
14478             inputblock.cls += (this.before) ? '' : ' input-group';
14479             
14480             inputblock.cn.push({
14481                 tag :'span',
14482                 cls : 'input-group-addon',
14483                 html : this.after
14484             });
14485         }
14486
14487         var box = {
14488             tag: 'div',
14489             cn: [
14490                 {
14491                     tag: 'input',
14492                     type : 'hidden',
14493                     cls: 'form-hidden-field'
14494                 },
14495                 inputblock
14496             ]
14497             
14498         };
14499         
14500         if(this.multiple){
14501             box = {
14502                 tag: 'div',
14503                 cn: [
14504                     {
14505                         tag: 'input',
14506                         type : 'hidden',
14507                         cls: 'form-hidden-field'
14508                     },
14509                     {
14510                         tag: 'ul',
14511                         cls: 'roo-select2-choices',
14512                         cn:[
14513                             {
14514                                 tag: 'li',
14515                                 cls: 'roo-select2-search-field',
14516                                 cn: [
14517
14518                                     inputblock
14519                                 ]
14520                             }
14521                         ]
14522                     }
14523                 ]
14524             }
14525         };
14526         
14527         var combobox = {
14528             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14529             cn: [
14530                 box
14531             ]
14532         };
14533         
14534         if(!this.multiple && this.showToggleBtn){
14535             
14536             var caret = {
14537                         tag: 'span',
14538                         cls: 'caret'
14539             };
14540             
14541             if (this.caret != false) {
14542                 caret = {
14543                      tag: 'i',
14544                      cls: 'fa fa-' + this.caret
14545                 };
14546                 
14547             }
14548             
14549             combobox.cn.push({
14550                 tag :'span',
14551                 cls : 'input-group-addon btn dropdown-toggle',
14552                 cn : [
14553                     caret,
14554                     {
14555                         tag: 'span',
14556                         cls: 'combobox-clear',
14557                         cn  : [
14558                             {
14559                                 tag : 'i',
14560                                 cls: 'icon-remove'
14561                             }
14562                         ]
14563                     }
14564                 ]
14565
14566             })
14567         }
14568         
14569         if(this.multiple){
14570             combobox.cls += ' roo-select2-container-multi';
14571         }
14572         
14573         var align = this.labelAlign || this.parentLabelAlign();
14574         
14575         if (align ==='left' && this.fieldLabel.length) {
14576
14577             cfg.cn = [
14578                 {
14579                    tag : 'i',
14580                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14581                    tooltip : 'This field is required'
14582                 },
14583                 {
14584                     tag: 'label',
14585                     cls : 'control-label',
14586                     html : this.fieldLabel
14587
14588                 },
14589                 {
14590                     cls : '', 
14591                     cn: [
14592                         combobox
14593                     ]
14594                 }
14595             ];
14596             
14597             var labelCfg = cfg.cn[1];
14598             var contentCfg = cfg.cn[2];
14599             
14600
14601             if(this.indicatorpos == 'right'){
14602                 cfg.cn = [
14603                     {
14604                         tag: 'label',
14605                         cls : 'control-label',
14606                         html : this.fieldLabel,
14607                         cn : [
14608                             {
14609                                tag : 'i',
14610                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14611                                tooltip : 'This field is required'
14612                             }
14613                         ]
14614                     },
14615                     {
14616                         cls : '', 
14617                         cn: [
14618                             combobox
14619                         ]
14620                     }
14621                 ];
14622             }
14623             
14624             labelCfg = cfg.cn[0];
14625             contentCfg = cfg.cn[2];
14626             
14627             if(this.labelWidth > 12){
14628                 labelCfg.style = "width: " + this.labelWidth + 'px';
14629             }
14630             
14631             if(this.labelWidth < 13 && this.labelmd == 0){
14632                 this.labelmd = this.labelWidth;
14633             }
14634             
14635             if(this.labellg > 0){
14636                 labelCfg.cls += ' col-lg-' + this.labellg;
14637                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14638             }
14639             
14640             if(this.labelmd > 0){
14641                 labelCfg.cls += ' col-md-' + this.labelmd;
14642                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14643             }
14644             
14645             if(this.labelsm > 0){
14646                 labelCfg.cls += ' col-sm-' + this.labelsm;
14647                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14648             }
14649             
14650             if(this.labelxs > 0){
14651                 labelCfg.cls += ' col-xs-' + this.labelxs;
14652                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14653             }
14654                 
14655                 
14656         } else if ( this.fieldLabel.length) {
14657             cfg.cn = [
14658                 {
14659                    tag : 'i',
14660                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14661                    tooltip : 'This field is required'
14662                 },
14663                 {
14664                     tag: 'label',
14665                     cls : 'control-label',
14666                     html : this.fieldLabel
14667
14668                 },
14669                 {
14670                     cls : '', 
14671                     cn: [
14672                         combobox
14673                     ]
14674                 }
14675             ];
14676             
14677             if(this.indicatorpos == 'right'){
14678                 cfg.cn = [
14679                     {
14680                         tag: 'label',
14681                         cls : 'control-label',
14682                         html : this.fieldLabel,
14683                         cn : [
14684                             {
14685                                tag : 'i',
14686                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14687                                tooltip : 'This field is required'
14688                             }
14689                         ]
14690                     },
14691                     {
14692                         cls : '', 
14693                         cn: [
14694                             combobox
14695                         ]
14696                     }
14697                 ];
14698             }
14699         } else {
14700             cfg.cn = combobox;    
14701         }
14702         
14703         
14704         var settings = this;
14705         
14706         ['xs','sm','md','lg'].map(function(size){
14707             if (settings[size]) {
14708                 cfg.cls += ' col-' + size + '-' + settings[size];
14709             }
14710         });
14711         
14712         return cfg;
14713     },
14714     
14715     initTouchView : function()
14716     {
14717         this.renderTouchView();
14718         
14719         this.touchViewEl.on('scroll', function(){
14720             this.el.dom.scrollTop = 0;
14721         }, this);
14722         
14723         this.originalValue = this.getValue();
14724         
14725         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14726         
14727         this.inputEl().on("click", this.showTouchView, this);
14728         if (this.triggerEl) {
14729             this.triggerEl.on("click", this.showTouchView, this);
14730         }
14731         
14732         
14733         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14734         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14735         
14736         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14737         
14738         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14739         this.store.on('load', this.onTouchViewLoad, this);
14740         this.store.on('loadexception', this.onTouchViewLoadException, this);
14741         
14742         if(this.hiddenName){
14743             
14744             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14745             
14746             this.hiddenField.dom.value =
14747                 this.hiddenValue !== undefined ? this.hiddenValue :
14748                 this.value !== undefined ? this.value : '';
14749         
14750             this.el.dom.removeAttribute('name');
14751             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14752         }
14753         
14754         if(this.multiple){
14755             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14756             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14757         }
14758         
14759         if(this.removable && !this.multiple){
14760             var close = this.closeTriggerEl();
14761             if(close){
14762                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14763                 close.on('click', this.removeBtnClick, this, close);
14764             }
14765         }
14766         /*
14767          * fix the bug in Safari iOS8
14768          */
14769         this.inputEl().on("focus", function(e){
14770             document.activeElement.blur();
14771         }, this);
14772         
14773         return;
14774         
14775         
14776     },
14777     
14778     renderTouchView : function()
14779     {
14780         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14781         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14782         
14783         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14784         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14785         
14786         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14787         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14788         this.touchViewBodyEl.setStyle('overflow', 'auto');
14789         
14790         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14791         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14792         
14793         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14794         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14795         
14796     },
14797     
14798     showTouchView : function()
14799     {
14800         if(this.disabled){
14801             return;
14802         }
14803         
14804         this.touchViewHeaderEl.hide();
14805
14806         if(this.modalTitle.length){
14807             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14808             this.touchViewHeaderEl.show();
14809         }
14810
14811         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14812         this.touchViewEl.show();
14813
14814         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14815         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14816                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14817
14818         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14819
14820         if(this.modalTitle.length){
14821             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14822         }
14823         
14824         this.touchViewBodyEl.setHeight(bodyHeight);
14825
14826         if(this.animate){
14827             var _this = this;
14828             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14829         }else{
14830             this.touchViewEl.addClass('in');
14831         }
14832
14833         this.doTouchViewQuery();
14834         
14835     },
14836     
14837     hideTouchView : function()
14838     {
14839         this.touchViewEl.removeClass('in');
14840
14841         if(this.animate){
14842             var _this = this;
14843             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14844         }else{
14845             this.touchViewEl.setStyle('display', 'none');
14846         }
14847         
14848     },
14849     
14850     setTouchViewValue : function()
14851     {
14852         if(this.multiple){
14853             this.clearItem();
14854         
14855             var _this = this;
14856
14857             Roo.each(this.tickItems, function(o){
14858                 this.addItem(o);
14859             }, this);
14860         }
14861         
14862         this.hideTouchView();
14863     },
14864     
14865     doTouchViewQuery : function()
14866     {
14867         var qe = {
14868             query: '',
14869             forceAll: true,
14870             combo: this,
14871             cancel:false
14872         };
14873         
14874         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14875             return false;
14876         }
14877         
14878         if(!this.alwaysQuery || this.mode == 'local'){
14879             this.onTouchViewLoad();
14880             return;
14881         }
14882         
14883         this.store.load();
14884     },
14885     
14886     onTouchViewBeforeLoad : function(combo,opts)
14887     {
14888         return;
14889     },
14890
14891     // private
14892     onTouchViewLoad : function()
14893     {
14894         if(this.store.getCount() < 1){
14895             this.onTouchViewEmptyResults();
14896             return;
14897         }
14898         
14899         this.clearTouchView();
14900         
14901         var rawValue = this.getRawValue();
14902         
14903         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14904         
14905         this.tickItems = [];
14906         
14907         this.store.data.each(function(d, rowIndex){
14908             var row = this.touchViewListGroup.createChild(template);
14909             
14910             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14911                 row.addClass(d.data.cls);
14912             }
14913             
14914             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14915                 var cfg = {
14916                     data : d.data,
14917                     html : d.data[this.displayField]
14918                 };
14919                 
14920                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14921                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14922                 }
14923             }
14924             row.removeClass('selected');
14925             if(!this.multiple && this.valueField &&
14926                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14927             {
14928                 // radio buttons..
14929                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14930                 row.addClass('selected');
14931             }
14932             
14933             if(this.multiple && this.valueField &&
14934                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14935             {
14936                 
14937                 // checkboxes...
14938                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14939                 this.tickItems.push(d.data);
14940             }
14941             
14942             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14943             
14944         }, this);
14945         
14946         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14947         
14948         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14949
14950         if(this.modalTitle.length){
14951             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14952         }
14953
14954         var listHeight = this.touchViewListGroup.getHeight();
14955         
14956         var _this = this;
14957         
14958         if(firstChecked && listHeight > bodyHeight){
14959             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14960         }
14961         
14962     },
14963     
14964     onTouchViewLoadException : function()
14965     {
14966         this.hideTouchView();
14967     },
14968     
14969     onTouchViewEmptyResults : function()
14970     {
14971         this.clearTouchView();
14972         
14973         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14974         
14975         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14976         
14977     },
14978     
14979     clearTouchView : function()
14980     {
14981         this.touchViewListGroup.dom.innerHTML = '';
14982     },
14983     
14984     onTouchViewClick : function(e, el, o)
14985     {
14986         e.preventDefault();
14987         
14988         var row = o.row;
14989         var rowIndex = o.rowIndex;
14990         
14991         var r = this.store.getAt(rowIndex);
14992         
14993         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14994             
14995             if(!this.multiple){
14996                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14997                     c.dom.removeAttribute('checked');
14998                 }, this);
14999
15000                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15001
15002                 this.setFromData(r.data);
15003
15004                 var close = this.closeTriggerEl();
15005
15006                 if(close){
15007                     close.show();
15008                 }
15009
15010                 this.hideTouchView();
15011
15012                 this.fireEvent('select', this, r, rowIndex);
15013
15014                 return;
15015             }
15016
15017             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15018                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15019                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15020                 return;
15021             }
15022
15023             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15024             this.addItem(r.data);
15025             this.tickItems.push(r.data);
15026         }
15027     },
15028     
15029     getAutoCreateNativeIOS : function()
15030     {
15031         var cfg = {
15032             cls: 'form-group' //input-group,
15033         };
15034         
15035         var combobox =  {
15036             tag: 'select',
15037             cls : 'roo-ios-select'
15038         };
15039         
15040         if (this.name) {
15041             combobox.name = this.name;
15042         }
15043         
15044         if (this.disabled) {
15045             combobox.disabled = true;
15046         }
15047         
15048         var settings = this;
15049         
15050         ['xs','sm','md','lg'].map(function(size){
15051             if (settings[size]) {
15052                 cfg.cls += ' col-' + size + '-' + settings[size];
15053             }
15054         });
15055         
15056         cfg.cn = combobox;
15057         
15058         return cfg;
15059         
15060     },
15061     
15062     initIOSView : function()
15063     {
15064         this.store.on('load', this.onIOSViewLoad, this);
15065         
15066         return;
15067     },
15068     
15069     onIOSViewLoad : function()
15070     {
15071         if(this.store.getCount() < 1){
15072             return;
15073         }
15074         
15075         this.clearIOSView();
15076         
15077         if(this.allowBlank) {
15078             
15079             var default_text = '-- SELECT --';
15080             
15081             var opt = this.inputEl().createChild({
15082                 tag: 'option',
15083                 value : 0,
15084                 html : default_text
15085             });
15086             
15087             var o = {};
15088             o[this.valueField] = 0;
15089             o[this.displayField] = default_text;
15090             
15091             this.ios_options.push({
15092                 data : o,
15093                 el : opt
15094             });
15095             
15096         }
15097         
15098         this.store.data.each(function(d, rowIndex){
15099             
15100             var html = '';
15101             
15102             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15103                 html = d.data[this.displayField];
15104             }
15105             
15106             var value = '';
15107             
15108             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15109                 value = d.data[this.valueField];
15110             }
15111             
15112             var option = {
15113                 tag: 'option',
15114                 value : value,
15115                 html : html
15116             };
15117             
15118             if(this.value == d.data[this.valueField]){
15119                 option['selected'] = true;
15120             }
15121             
15122             var opt = this.inputEl().createChild(option);
15123             
15124             this.ios_options.push({
15125                 data : d.data,
15126                 el : opt
15127             });
15128             
15129         }, this);
15130         
15131         this.inputEl().on('change', function(){
15132            this.fireEvent('select', this);
15133         }, this);
15134         
15135     },
15136     
15137     clearIOSView: function()
15138     {
15139         this.inputEl().dom.innerHTML = '';
15140         
15141         this.ios_options = [];
15142     },
15143     
15144     setIOSValue: function(v)
15145     {
15146         this.value = v;
15147         
15148         if(!this.ios_options){
15149             return;
15150         }
15151         
15152         Roo.each(this.ios_options, function(opts){
15153            
15154            opts.el.dom.removeAttribute('selected');
15155            
15156            if(opts.data[this.valueField] != v){
15157                return;
15158            }
15159            
15160            opts.el.dom.setAttribute('selected', true);
15161            
15162         }, this);
15163     }
15164
15165     /** 
15166     * @cfg {Boolean} grow 
15167     * @hide 
15168     */
15169     /** 
15170     * @cfg {Number} growMin 
15171     * @hide 
15172     */
15173     /** 
15174     * @cfg {Number} growMax 
15175     * @hide 
15176     */
15177     /**
15178      * @hide
15179      * @method autoSize
15180      */
15181 });
15182
15183 Roo.apply(Roo.bootstrap.ComboBox,  {
15184     
15185     header : {
15186         tag: 'div',
15187         cls: 'modal-header',
15188         cn: [
15189             {
15190                 tag: 'h4',
15191                 cls: 'modal-title'
15192             }
15193         ]
15194     },
15195     
15196     body : {
15197         tag: 'div',
15198         cls: 'modal-body',
15199         cn: [
15200             {
15201                 tag: 'ul',
15202                 cls: 'list-group'
15203             }
15204         ]
15205     },
15206     
15207     listItemRadio : {
15208         tag: 'li',
15209         cls: 'list-group-item',
15210         cn: [
15211             {
15212                 tag: 'span',
15213                 cls: 'roo-combobox-list-group-item-value'
15214             },
15215             {
15216                 tag: 'div',
15217                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15218                 cn: [
15219                     {
15220                         tag: 'input',
15221                         type: 'radio'
15222                     },
15223                     {
15224                         tag: 'label'
15225                     }
15226                 ]
15227             }
15228         ]
15229     },
15230     
15231     listItemCheckbox : {
15232         tag: 'li',
15233         cls: 'list-group-item',
15234         cn: [
15235             {
15236                 tag: 'span',
15237                 cls: 'roo-combobox-list-group-item-value'
15238             },
15239             {
15240                 tag: 'div',
15241                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15242                 cn: [
15243                     {
15244                         tag: 'input',
15245                         type: 'checkbox'
15246                     },
15247                     {
15248                         tag: 'label'
15249                     }
15250                 ]
15251             }
15252         ]
15253     },
15254     
15255     emptyResult : {
15256         tag: 'div',
15257         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15258     },
15259     
15260     footer : {
15261         tag: 'div',
15262         cls: 'modal-footer',
15263         cn: [
15264             {
15265                 tag: 'div',
15266                 cls: 'row',
15267                 cn: [
15268                     {
15269                         tag: 'div',
15270                         cls: 'col-xs-6 text-left',
15271                         cn: {
15272                             tag: 'button',
15273                             cls: 'btn btn-danger roo-touch-view-cancel',
15274                             html: 'Cancel'
15275                         }
15276                     },
15277                     {
15278                         tag: 'div',
15279                         cls: 'col-xs-6 text-right',
15280                         cn: {
15281                             tag: 'button',
15282                             cls: 'btn btn-success roo-touch-view-ok',
15283                             html: 'OK'
15284                         }
15285                     }
15286                 ]
15287             }
15288         ]
15289         
15290     }
15291 });
15292
15293 Roo.apply(Roo.bootstrap.ComboBox,  {
15294     
15295     touchViewTemplate : {
15296         tag: 'div',
15297         cls: 'modal fade roo-combobox-touch-view',
15298         cn: [
15299             {
15300                 tag: 'div',
15301                 cls: 'modal-dialog',
15302                 style : 'position:fixed', // we have to fix position....
15303                 cn: [
15304                     {
15305                         tag: 'div',
15306                         cls: 'modal-content',
15307                         cn: [
15308                             Roo.bootstrap.ComboBox.header,
15309                             Roo.bootstrap.ComboBox.body,
15310                             Roo.bootstrap.ComboBox.footer
15311                         ]
15312                     }
15313                 ]
15314             }
15315         ]
15316     }
15317 });/*
15318  * Based on:
15319  * Ext JS Library 1.1.1
15320  * Copyright(c) 2006-2007, Ext JS, LLC.
15321  *
15322  * Originally Released Under LGPL - original licence link has changed is not relivant.
15323  *
15324  * Fork - LGPL
15325  * <script type="text/javascript">
15326  */
15327
15328 /**
15329  * @class Roo.View
15330  * @extends Roo.util.Observable
15331  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15332  * This class also supports single and multi selection modes. <br>
15333  * Create a data model bound view:
15334  <pre><code>
15335  var store = new Roo.data.Store(...);
15336
15337  var view = new Roo.View({
15338     el : "my-element",
15339     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15340  
15341     singleSelect: true,
15342     selectedClass: "ydataview-selected",
15343     store: store
15344  });
15345
15346  // listen for node click?
15347  view.on("click", function(vw, index, node, e){
15348  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15349  });
15350
15351  // load XML data
15352  dataModel.load("foobar.xml");
15353  </code></pre>
15354  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15355  * <br><br>
15356  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15357  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15358  * 
15359  * Note: old style constructor is still suported (container, template, config)
15360  * 
15361  * @constructor
15362  * Create a new View
15363  * @param {Object} config The config object
15364  * 
15365  */
15366 Roo.View = function(config, depreciated_tpl, depreciated_config){
15367     
15368     this.parent = false;
15369     
15370     if (typeof(depreciated_tpl) == 'undefined') {
15371         // new way.. - universal constructor.
15372         Roo.apply(this, config);
15373         this.el  = Roo.get(this.el);
15374     } else {
15375         // old format..
15376         this.el  = Roo.get(config);
15377         this.tpl = depreciated_tpl;
15378         Roo.apply(this, depreciated_config);
15379     }
15380     this.wrapEl  = this.el.wrap().wrap();
15381     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15382     
15383     
15384     if(typeof(this.tpl) == "string"){
15385         this.tpl = new Roo.Template(this.tpl);
15386     } else {
15387         // support xtype ctors..
15388         this.tpl = new Roo.factory(this.tpl, Roo);
15389     }
15390     
15391     
15392     this.tpl.compile();
15393     
15394     /** @private */
15395     this.addEvents({
15396         /**
15397          * @event beforeclick
15398          * Fires before a click is processed. Returns false to cancel the default action.
15399          * @param {Roo.View} this
15400          * @param {Number} index The index of the target node
15401          * @param {HTMLElement} node The target node
15402          * @param {Roo.EventObject} e The raw event object
15403          */
15404             "beforeclick" : true,
15405         /**
15406          * @event click
15407          * Fires when a template node is clicked.
15408          * @param {Roo.View} this
15409          * @param {Number} index The index of the target node
15410          * @param {HTMLElement} node The target node
15411          * @param {Roo.EventObject} e The raw event object
15412          */
15413             "click" : true,
15414         /**
15415          * @event dblclick
15416          * Fires when a template node is double clicked.
15417          * @param {Roo.View} this
15418          * @param {Number} index The index of the target node
15419          * @param {HTMLElement} node The target node
15420          * @param {Roo.EventObject} e The raw event object
15421          */
15422             "dblclick" : true,
15423         /**
15424          * @event contextmenu
15425          * Fires when a template node is right clicked.
15426          * @param {Roo.View} this
15427          * @param {Number} index The index of the target node
15428          * @param {HTMLElement} node The target node
15429          * @param {Roo.EventObject} e The raw event object
15430          */
15431             "contextmenu" : true,
15432         /**
15433          * @event selectionchange
15434          * Fires when the selected nodes change.
15435          * @param {Roo.View} this
15436          * @param {Array} selections Array of the selected nodes
15437          */
15438             "selectionchange" : true,
15439     
15440         /**
15441          * @event beforeselect
15442          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15443          * @param {Roo.View} this
15444          * @param {HTMLElement} node The node to be selected
15445          * @param {Array} selections Array of currently selected nodes
15446          */
15447             "beforeselect" : true,
15448         /**
15449          * @event preparedata
15450          * Fires on every row to render, to allow you to change the data.
15451          * @param {Roo.View} this
15452          * @param {Object} data to be rendered (change this)
15453          */
15454           "preparedata" : true
15455           
15456           
15457         });
15458
15459
15460
15461     this.el.on({
15462         "click": this.onClick,
15463         "dblclick": this.onDblClick,
15464         "contextmenu": this.onContextMenu,
15465         scope:this
15466     });
15467
15468     this.selections = [];
15469     this.nodes = [];
15470     this.cmp = new Roo.CompositeElementLite([]);
15471     if(this.store){
15472         this.store = Roo.factory(this.store, Roo.data);
15473         this.setStore(this.store, true);
15474     }
15475     
15476     if ( this.footer && this.footer.xtype) {
15477            
15478          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15479         
15480         this.footer.dataSource = this.store;
15481         this.footer.container = fctr;
15482         this.footer = Roo.factory(this.footer, Roo);
15483         fctr.insertFirst(this.el);
15484         
15485         // this is a bit insane - as the paging toolbar seems to detach the el..
15486 //        dom.parentNode.parentNode.parentNode
15487          // they get detached?
15488     }
15489     
15490     
15491     Roo.View.superclass.constructor.call(this);
15492     
15493     
15494 };
15495
15496 Roo.extend(Roo.View, Roo.util.Observable, {
15497     
15498      /**
15499      * @cfg {Roo.data.Store} store Data store to load data from.
15500      */
15501     store : false,
15502     
15503     /**
15504      * @cfg {String|Roo.Element} el The container element.
15505      */
15506     el : '',
15507     
15508     /**
15509      * @cfg {String|Roo.Template} tpl The template used by this View 
15510      */
15511     tpl : false,
15512     /**
15513      * @cfg {String} dataName the named area of the template to use as the data area
15514      *                          Works with domtemplates roo-name="name"
15515      */
15516     dataName: false,
15517     /**
15518      * @cfg {String} selectedClass The css class to add to selected nodes
15519      */
15520     selectedClass : "x-view-selected",
15521      /**
15522      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15523      */
15524     emptyText : "",
15525     
15526     /**
15527      * @cfg {String} text to display on mask (default Loading)
15528      */
15529     mask : false,
15530     /**
15531      * @cfg {Boolean} multiSelect Allow multiple selection
15532      */
15533     multiSelect : false,
15534     /**
15535      * @cfg {Boolean} singleSelect Allow single selection
15536      */
15537     singleSelect:  false,
15538     
15539     /**
15540      * @cfg {Boolean} toggleSelect - selecting 
15541      */
15542     toggleSelect : false,
15543     
15544     /**
15545      * @cfg {Boolean} tickable - selecting 
15546      */
15547     tickable : false,
15548     
15549     /**
15550      * Returns the element this view is bound to.
15551      * @return {Roo.Element}
15552      */
15553     getEl : function(){
15554         return this.wrapEl;
15555     },
15556     
15557     
15558
15559     /**
15560      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15561      */
15562     refresh : function(){
15563         //Roo.log('refresh');
15564         var t = this.tpl;
15565         
15566         // if we are using something like 'domtemplate', then
15567         // the what gets used is:
15568         // t.applySubtemplate(NAME, data, wrapping data..)
15569         // the outer template then get' applied with
15570         //     the store 'extra data'
15571         // and the body get's added to the
15572         //      roo-name="data" node?
15573         //      <span class='roo-tpl-{name}'></span> ?????
15574         
15575         
15576         
15577         this.clearSelections();
15578         this.el.update("");
15579         var html = [];
15580         var records = this.store.getRange();
15581         if(records.length < 1) {
15582             
15583             // is this valid??  = should it render a template??
15584             
15585             this.el.update(this.emptyText);
15586             return;
15587         }
15588         var el = this.el;
15589         if (this.dataName) {
15590             this.el.update(t.apply(this.store.meta)); //????
15591             el = this.el.child('.roo-tpl-' + this.dataName);
15592         }
15593         
15594         for(var i = 0, len = records.length; i < len; i++){
15595             var data = this.prepareData(records[i].data, i, records[i]);
15596             this.fireEvent("preparedata", this, data, i, records[i]);
15597             
15598             var d = Roo.apply({}, data);
15599             
15600             if(this.tickable){
15601                 Roo.apply(d, {'roo-id' : Roo.id()});
15602                 
15603                 var _this = this;
15604             
15605                 Roo.each(this.parent.item, function(item){
15606                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15607                         return;
15608                     }
15609                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15610                 });
15611             }
15612             
15613             html[html.length] = Roo.util.Format.trim(
15614                 this.dataName ?
15615                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15616                     t.apply(d)
15617             );
15618         }
15619         
15620         
15621         
15622         el.update(html.join(""));
15623         this.nodes = el.dom.childNodes;
15624         this.updateIndexes(0);
15625     },
15626     
15627
15628     /**
15629      * Function to override to reformat the data that is sent to
15630      * the template for each node.
15631      * DEPRICATED - use the preparedata event handler.
15632      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15633      * a JSON object for an UpdateManager bound view).
15634      */
15635     prepareData : function(data, index, record)
15636     {
15637         this.fireEvent("preparedata", this, data, index, record);
15638         return data;
15639     },
15640
15641     onUpdate : function(ds, record){
15642         // Roo.log('on update');   
15643         this.clearSelections();
15644         var index = this.store.indexOf(record);
15645         var n = this.nodes[index];
15646         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15647         n.parentNode.removeChild(n);
15648         this.updateIndexes(index, index);
15649     },
15650
15651     
15652     
15653 // --------- FIXME     
15654     onAdd : function(ds, records, index)
15655     {
15656         //Roo.log(['on Add', ds, records, index] );        
15657         this.clearSelections();
15658         if(this.nodes.length == 0){
15659             this.refresh();
15660             return;
15661         }
15662         var n = this.nodes[index];
15663         for(var i = 0, len = records.length; i < len; i++){
15664             var d = this.prepareData(records[i].data, i, records[i]);
15665             if(n){
15666                 this.tpl.insertBefore(n, d);
15667             }else{
15668                 
15669                 this.tpl.append(this.el, d);
15670             }
15671         }
15672         this.updateIndexes(index);
15673     },
15674
15675     onRemove : function(ds, record, index){
15676        // Roo.log('onRemove');
15677         this.clearSelections();
15678         var el = this.dataName  ?
15679             this.el.child('.roo-tpl-' + this.dataName) :
15680             this.el; 
15681         
15682         el.dom.removeChild(this.nodes[index]);
15683         this.updateIndexes(index);
15684     },
15685
15686     /**
15687      * Refresh an individual node.
15688      * @param {Number} index
15689      */
15690     refreshNode : function(index){
15691         this.onUpdate(this.store, this.store.getAt(index));
15692     },
15693
15694     updateIndexes : function(startIndex, endIndex){
15695         var ns = this.nodes;
15696         startIndex = startIndex || 0;
15697         endIndex = endIndex || ns.length - 1;
15698         for(var i = startIndex; i <= endIndex; i++){
15699             ns[i].nodeIndex = i;
15700         }
15701     },
15702
15703     /**
15704      * Changes the data store this view uses and refresh the view.
15705      * @param {Store} store
15706      */
15707     setStore : function(store, initial){
15708         if(!initial && this.store){
15709             this.store.un("datachanged", this.refresh);
15710             this.store.un("add", this.onAdd);
15711             this.store.un("remove", this.onRemove);
15712             this.store.un("update", this.onUpdate);
15713             this.store.un("clear", this.refresh);
15714             this.store.un("beforeload", this.onBeforeLoad);
15715             this.store.un("load", this.onLoad);
15716             this.store.un("loadexception", this.onLoad);
15717         }
15718         if(store){
15719           
15720             store.on("datachanged", this.refresh, this);
15721             store.on("add", this.onAdd, this);
15722             store.on("remove", this.onRemove, this);
15723             store.on("update", this.onUpdate, this);
15724             store.on("clear", this.refresh, this);
15725             store.on("beforeload", this.onBeforeLoad, this);
15726             store.on("load", this.onLoad, this);
15727             store.on("loadexception", this.onLoad, this);
15728         }
15729         
15730         if(store){
15731             this.refresh();
15732         }
15733     },
15734     /**
15735      * onbeforeLoad - masks the loading area.
15736      *
15737      */
15738     onBeforeLoad : function(store,opts)
15739     {
15740          //Roo.log('onBeforeLoad');   
15741         if (!opts.add) {
15742             this.el.update("");
15743         }
15744         this.el.mask(this.mask ? this.mask : "Loading" ); 
15745     },
15746     onLoad : function ()
15747     {
15748         this.el.unmask();
15749     },
15750     
15751
15752     /**
15753      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15754      * @param {HTMLElement} node
15755      * @return {HTMLElement} The template node
15756      */
15757     findItemFromChild : function(node){
15758         var el = this.dataName  ?
15759             this.el.child('.roo-tpl-' + this.dataName,true) :
15760             this.el.dom; 
15761         
15762         if(!node || node.parentNode == el){
15763                     return node;
15764             }
15765             var p = node.parentNode;
15766             while(p && p != el){
15767             if(p.parentNode == el){
15768                 return p;
15769             }
15770             p = p.parentNode;
15771         }
15772             return null;
15773     },
15774
15775     /** @ignore */
15776     onClick : function(e){
15777         var item = this.findItemFromChild(e.getTarget());
15778         if(item){
15779             var index = this.indexOf(item);
15780             if(this.onItemClick(item, index, e) !== false){
15781                 this.fireEvent("click", this, index, item, e);
15782             }
15783         }else{
15784             this.clearSelections();
15785         }
15786     },
15787
15788     /** @ignore */
15789     onContextMenu : function(e){
15790         var item = this.findItemFromChild(e.getTarget());
15791         if(item){
15792             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15793         }
15794     },
15795
15796     /** @ignore */
15797     onDblClick : function(e){
15798         var item = this.findItemFromChild(e.getTarget());
15799         if(item){
15800             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15801         }
15802     },
15803
15804     onItemClick : function(item, index, e)
15805     {
15806         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15807             return false;
15808         }
15809         if (this.toggleSelect) {
15810             var m = this.isSelected(item) ? 'unselect' : 'select';
15811             //Roo.log(m);
15812             var _t = this;
15813             _t[m](item, true, false);
15814             return true;
15815         }
15816         if(this.multiSelect || this.singleSelect){
15817             if(this.multiSelect && e.shiftKey && this.lastSelection){
15818                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15819             }else{
15820                 this.select(item, this.multiSelect && e.ctrlKey);
15821                 this.lastSelection = item;
15822             }
15823             
15824             if(!this.tickable){
15825                 e.preventDefault();
15826             }
15827             
15828         }
15829         return true;
15830     },
15831
15832     /**
15833      * Get the number of selected nodes.
15834      * @return {Number}
15835      */
15836     getSelectionCount : function(){
15837         return this.selections.length;
15838     },
15839
15840     /**
15841      * Get the currently selected nodes.
15842      * @return {Array} An array of HTMLElements
15843      */
15844     getSelectedNodes : function(){
15845         return this.selections;
15846     },
15847
15848     /**
15849      * Get the indexes of the selected nodes.
15850      * @return {Array}
15851      */
15852     getSelectedIndexes : function(){
15853         var indexes = [], s = this.selections;
15854         for(var i = 0, len = s.length; i < len; i++){
15855             indexes.push(s[i].nodeIndex);
15856         }
15857         return indexes;
15858     },
15859
15860     /**
15861      * Clear all selections
15862      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15863      */
15864     clearSelections : function(suppressEvent){
15865         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15866             this.cmp.elements = this.selections;
15867             this.cmp.removeClass(this.selectedClass);
15868             this.selections = [];
15869             if(!suppressEvent){
15870                 this.fireEvent("selectionchange", this, this.selections);
15871             }
15872         }
15873     },
15874
15875     /**
15876      * Returns true if the passed node is selected
15877      * @param {HTMLElement/Number} node The node or node index
15878      * @return {Boolean}
15879      */
15880     isSelected : function(node){
15881         var s = this.selections;
15882         if(s.length < 1){
15883             return false;
15884         }
15885         node = this.getNode(node);
15886         return s.indexOf(node) !== -1;
15887     },
15888
15889     /**
15890      * Selects nodes.
15891      * @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
15892      * @param {Boolean} keepExisting (optional) true to keep existing selections
15893      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15894      */
15895     select : function(nodeInfo, keepExisting, suppressEvent){
15896         if(nodeInfo instanceof Array){
15897             if(!keepExisting){
15898                 this.clearSelections(true);
15899             }
15900             for(var i = 0, len = nodeInfo.length; i < len; i++){
15901                 this.select(nodeInfo[i], true, true);
15902             }
15903             return;
15904         } 
15905         var node = this.getNode(nodeInfo);
15906         if(!node || this.isSelected(node)){
15907             return; // already selected.
15908         }
15909         if(!keepExisting){
15910             this.clearSelections(true);
15911         }
15912         
15913         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15914             Roo.fly(node).addClass(this.selectedClass);
15915             this.selections.push(node);
15916             if(!suppressEvent){
15917                 this.fireEvent("selectionchange", this, this.selections);
15918             }
15919         }
15920         
15921         
15922     },
15923       /**
15924      * Unselects nodes.
15925      * @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
15926      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15927      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15928      */
15929     unselect : function(nodeInfo, keepExisting, suppressEvent)
15930     {
15931         if(nodeInfo instanceof Array){
15932             Roo.each(this.selections, function(s) {
15933                 this.unselect(s, nodeInfo);
15934             }, this);
15935             return;
15936         }
15937         var node = this.getNode(nodeInfo);
15938         if(!node || !this.isSelected(node)){
15939             //Roo.log("not selected");
15940             return; // not selected.
15941         }
15942         // fireevent???
15943         var ns = [];
15944         Roo.each(this.selections, function(s) {
15945             if (s == node ) {
15946                 Roo.fly(node).removeClass(this.selectedClass);
15947
15948                 return;
15949             }
15950             ns.push(s);
15951         },this);
15952         
15953         this.selections= ns;
15954         this.fireEvent("selectionchange", this, this.selections);
15955     },
15956
15957     /**
15958      * Gets a template node.
15959      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15960      * @return {HTMLElement} The node or null if it wasn't found
15961      */
15962     getNode : function(nodeInfo){
15963         if(typeof nodeInfo == "string"){
15964             return document.getElementById(nodeInfo);
15965         }else if(typeof nodeInfo == "number"){
15966             return this.nodes[nodeInfo];
15967         }
15968         return nodeInfo;
15969     },
15970
15971     /**
15972      * Gets a range template nodes.
15973      * @param {Number} startIndex
15974      * @param {Number} endIndex
15975      * @return {Array} An array of nodes
15976      */
15977     getNodes : function(start, end){
15978         var ns = this.nodes;
15979         start = start || 0;
15980         end = typeof end == "undefined" ? ns.length - 1 : end;
15981         var nodes = [];
15982         if(start <= end){
15983             for(var i = start; i <= end; i++){
15984                 nodes.push(ns[i]);
15985             }
15986         } else{
15987             for(var i = start; i >= end; i--){
15988                 nodes.push(ns[i]);
15989             }
15990         }
15991         return nodes;
15992     },
15993
15994     /**
15995      * Finds the index of the passed node
15996      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15997      * @return {Number} The index of the node or -1
15998      */
15999     indexOf : function(node){
16000         node = this.getNode(node);
16001         if(typeof node.nodeIndex == "number"){
16002             return node.nodeIndex;
16003         }
16004         var ns = this.nodes;
16005         for(var i = 0, len = ns.length; i < len; i++){
16006             if(ns[i] == node){
16007                 return i;
16008             }
16009         }
16010         return -1;
16011     }
16012 });
16013 /*
16014  * - LGPL
16015  *
16016  * based on jquery fullcalendar
16017  * 
16018  */
16019
16020 Roo.bootstrap = Roo.bootstrap || {};
16021 /**
16022  * @class Roo.bootstrap.Calendar
16023  * @extends Roo.bootstrap.Component
16024  * Bootstrap Calendar class
16025  * @cfg {Boolean} loadMask (true|false) default false
16026  * @cfg {Object} header generate the user specific header of the calendar, default false
16027
16028  * @constructor
16029  * Create a new Container
16030  * @param {Object} config The config object
16031  */
16032
16033
16034
16035 Roo.bootstrap.Calendar = function(config){
16036     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16037      this.addEvents({
16038         /**
16039              * @event select
16040              * Fires when a date is selected
16041              * @param {DatePicker} this
16042              * @param {Date} date The selected date
16043              */
16044         'select': true,
16045         /**
16046              * @event monthchange
16047              * Fires when the displayed month changes 
16048              * @param {DatePicker} this
16049              * @param {Date} date The selected month
16050              */
16051         'monthchange': true,
16052         /**
16053              * @event evententer
16054              * Fires when mouse over an event
16055              * @param {Calendar} this
16056              * @param {event} Event
16057              */
16058         'evententer': true,
16059         /**
16060              * @event eventleave
16061              * Fires when the mouse leaves an
16062              * @param {Calendar} this
16063              * @param {event}
16064              */
16065         'eventleave': true,
16066         /**
16067              * @event eventclick
16068              * Fires when the mouse click an
16069              * @param {Calendar} this
16070              * @param {event}
16071              */
16072         'eventclick': true
16073         
16074     });
16075
16076 };
16077
16078 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16079     
16080      /**
16081      * @cfg {Number} startDay
16082      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16083      */
16084     startDay : 0,
16085     
16086     loadMask : false,
16087     
16088     header : false,
16089       
16090     getAutoCreate : function(){
16091         
16092         
16093         var fc_button = function(name, corner, style, content ) {
16094             return Roo.apply({},{
16095                 tag : 'span',
16096                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16097                          (corner.length ?
16098                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16099                             ''
16100                         ),
16101                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16102                 unselectable: 'on'
16103             });
16104         };
16105         
16106         var header = {};
16107         
16108         if(!this.header){
16109             header = {
16110                 tag : 'table',
16111                 cls : 'fc-header',
16112                 style : 'width:100%',
16113                 cn : [
16114                     {
16115                         tag: 'tr',
16116                         cn : [
16117                             {
16118                                 tag : 'td',
16119                                 cls : 'fc-header-left',
16120                                 cn : [
16121                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16122                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16123                                     { tag: 'span', cls: 'fc-header-space' },
16124                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16125
16126
16127                                 ]
16128                             },
16129
16130                             {
16131                                 tag : 'td',
16132                                 cls : 'fc-header-center',
16133                                 cn : [
16134                                     {
16135                                         tag: 'span',
16136                                         cls: 'fc-header-title',
16137                                         cn : {
16138                                             tag: 'H2',
16139                                             html : 'month / year'
16140                                         }
16141                                     }
16142
16143                                 ]
16144                             },
16145                             {
16146                                 tag : 'td',
16147                                 cls : 'fc-header-right',
16148                                 cn : [
16149                               /*      fc_button('month', 'left', '', 'month' ),
16150                                     fc_button('week', '', '', 'week' ),
16151                                     fc_button('day', 'right', '', 'day' )
16152                                 */    
16153
16154                                 ]
16155                             }
16156
16157                         ]
16158                     }
16159                 ]
16160             };
16161         }
16162         
16163         header = this.header;
16164         
16165        
16166         var cal_heads = function() {
16167             var ret = [];
16168             // fixme - handle this.
16169             
16170             for (var i =0; i < Date.dayNames.length; i++) {
16171                 var d = Date.dayNames[i];
16172                 ret.push({
16173                     tag: 'th',
16174                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16175                     html : d.substring(0,3)
16176                 });
16177                 
16178             }
16179             ret[0].cls += ' fc-first';
16180             ret[6].cls += ' fc-last';
16181             return ret;
16182         };
16183         var cal_cell = function(n) {
16184             return  {
16185                 tag: 'td',
16186                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16187                 cn : [
16188                     {
16189                         cn : [
16190                             {
16191                                 cls: 'fc-day-number',
16192                                 html: 'D'
16193                             },
16194                             {
16195                                 cls: 'fc-day-content',
16196                              
16197                                 cn : [
16198                                      {
16199                                         style: 'position: relative;' // height: 17px;
16200                                     }
16201                                 ]
16202                             }
16203                             
16204                             
16205                         ]
16206                     }
16207                 ]
16208                 
16209             }
16210         };
16211         var cal_rows = function() {
16212             
16213             var ret = [];
16214             for (var r = 0; r < 6; r++) {
16215                 var row= {
16216                     tag : 'tr',
16217                     cls : 'fc-week',
16218                     cn : []
16219                 };
16220                 
16221                 for (var i =0; i < Date.dayNames.length; i++) {
16222                     var d = Date.dayNames[i];
16223                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16224
16225                 }
16226                 row.cn[0].cls+=' fc-first';
16227                 row.cn[0].cn[0].style = 'min-height:90px';
16228                 row.cn[6].cls+=' fc-last';
16229                 ret.push(row);
16230                 
16231             }
16232             ret[0].cls += ' fc-first';
16233             ret[4].cls += ' fc-prev-last';
16234             ret[5].cls += ' fc-last';
16235             return ret;
16236             
16237         };
16238         
16239         var cal_table = {
16240             tag: 'table',
16241             cls: 'fc-border-separate',
16242             style : 'width:100%',
16243             cellspacing  : 0,
16244             cn : [
16245                 { 
16246                     tag: 'thead',
16247                     cn : [
16248                         { 
16249                             tag: 'tr',
16250                             cls : 'fc-first fc-last',
16251                             cn : cal_heads()
16252                         }
16253                     ]
16254                 },
16255                 { 
16256                     tag: 'tbody',
16257                     cn : cal_rows()
16258                 }
16259                   
16260             ]
16261         };
16262          
16263          var cfg = {
16264             cls : 'fc fc-ltr',
16265             cn : [
16266                 header,
16267                 {
16268                     cls : 'fc-content',
16269                     style : "position: relative;",
16270                     cn : [
16271                         {
16272                             cls : 'fc-view fc-view-month fc-grid',
16273                             style : 'position: relative',
16274                             unselectable : 'on',
16275                             cn : [
16276                                 {
16277                                     cls : 'fc-event-container',
16278                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16279                                 },
16280                                 cal_table
16281                             ]
16282                         }
16283                     ]
16284     
16285                 }
16286            ] 
16287             
16288         };
16289         
16290          
16291         
16292         return cfg;
16293     },
16294     
16295     
16296     initEvents : function()
16297     {
16298         if(!this.store){
16299             throw "can not find store for calendar";
16300         }
16301         
16302         var mark = {
16303             tag: "div",
16304             cls:"x-dlg-mask",
16305             style: "text-align:center",
16306             cn: [
16307                 {
16308                     tag: "div",
16309                     style: "background-color:white;width:50%;margin:250 auto",
16310                     cn: [
16311                         {
16312                             tag: "img",
16313                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16314                         },
16315                         {
16316                             tag: "span",
16317                             html: "Loading"
16318                         }
16319                         
16320                     ]
16321                 }
16322             ]
16323         };
16324         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16325         
16326         var size = this.el.select('.fc-content', true).first().getSize();
16327         this.maskEl.setSize(size.width, size.height);
16328         this.maskEl.enableDisplayMode("block");
16329         if(!this.loadMask){
16330             this.maskEl.hide();
16331         }
16332         
16333         this.store = Roo.factory(this.store, Roo.data);
16334         this.store.on('load', this.onLoad, this);
16335         this.store.on('beforeload', this.onBeforeLoad, this);
16336         
16337         this.resize();
16338         
16339         this.cells = this.el.select('.fc-day',true);
16340         //Roo.log(this.cells);
16341         this.textNodes = this.el.query('.fc-day-number');
16342         this.cells.addClassOnOver('fc-state-hover');
16343         
16344         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16345         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16346         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16347         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16348         
16349         this.on('monthchange', this.onMonthChange, this);
16350         
16351         this.update(new Date().clearTime());
16352     },
16353     
16354     resize : function() {
16355         var sz  = this.el.getSize();
16356         
16357         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16358         this.el.select('.fc-day-content div',true).setHeight(34);
16359     },
16360     
16361     
16362     // private
16363     showPrevMonth : function(e){
16364         this.update(this.activeDate.add("mo", -1));
16365     },
16366     showToday : function(e){
16367         this.update(new Date().clearTime());
16368     },
16369     // private
16370     showNextMonth : function(e){
16371         this.update(this.activeDate.add("mo", 1));
16372     },
16373
16374     // private
16375     showPrevYear : function(){
16376         this.update(this.activeDate.add("y", -1));
16377     },
16378
16379     // private
16380     showNextYear : function(){
16381         this.update(this.activeDate.add("y", 1));
16382     },
16383
16384     
16385    // private
16386     update : function(date)
16387     {
16388         var vd = this.activeDate;
16389         this.activeDate = date;
16390 //        if(vd && this.el){
16391 //            var t = date.getTime();
16392 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16393 //                Roo.log('using add remove');
16394 //                
16395 //                this.fireEvent('monthchange', this, date);
16396 //                
16397 //                this.cells.removeClass("fc-state-highlight");
16398 //                this.cells.each(function(c){
16399 //                   if(c.dateValue == t){
16400 //                       c.addClass("fc-state-highlight");
16401 //                       setTimeout(function(){
16402 //                            try{c.dom.firstChild.focus();}catch(e){}
16403 //                       }, 50);
16404 //                       return false;
16405 //                   }
16406 //                   return true;
16407 //                });
16408 //                return;
16409 //            }
16410 //        }
16411         
16412         var days = date.getDaysInMonth();
16413         
16414         var firstOfMonth = date.getFirstDateOfMonth();
16415         var startingPos = firstOfMonth.getDay()-this.startDay;
16416         
16417         if(startingPos < this.startDay){
16418             startingPos += 7;
16419         }
16420         
16421         var pm = date.add(Date.MONTH, -1);
16422         var prevStart = pm.getDaysInMonth()-startingPos;
16423 //        
16424         this.cells = this.el.select('.fc-day',true);
16425         this.textNodes = this.el.query('.fc-day-number');
16426         this.cells.addClassOnOver('fc-state-hover');
16427         
16428         var cells = this.cells.elements;
16429         var textEls = this.textNodes;
16430         
16431         Roo.each(cells, function(cell){
16432             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16433         });
16434         
16435         days += startingPos;
16436
16437         // convert everything to numbers so it's fast
16438         var day = 86400000;
16439         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16440         //Roo.log(d);
16441         //Roo.log(pm);
16442         //Roo.log(prevStart);
16443         
16444         var today = new Date().clearTime().getTime();
16445         var sel = date.clearTime().getTime();
16446         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16447         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16448         var ddMatch = this.disabledDatesRE;
16449         var ddText = this.disabledDatesText;
16450         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16451         var ddaysText = this.disabledDaysText;
16452         var format = this.format;
16453         
16454         var setCellClass = function(cal, cell){
16455             cell.row = 0;
16456             cell.events = [];
16457             cell.more = [];
16458             //Roo.log('set Cell Class');
16459             cell.title = "";
16460             var t = d.getTime();
16461             
16462             //Roo.log(d);
16463             
16464             cell.dateValue = t;
16465             if(t == today){
16466                 cell.className += " fc-today";
16467                 cell.className += " fc-state-highlight";
16468                 cell.title = cal.todayText;
16469             }
16470             if(t == sel){
16471                 // disable highlight in other month..
16472                 //cell.className += " fc-state-highlight";
16473                 
16474             }
16475             // disabling
16476             if(t < min) {
16477                 cell.className = " fc-state-disabled";
16478                 cell.title = cal.minText;
16479                 return;
16480             }
16481             if(t > max) {
16482                 cell.className = " fc-state-disabled";
16483                 cell.title = cal.maxText;
16484                 return;
16485             }
16486             if(ddays){
16487                 if(ddays.indexOf(d.getDay()) != -1){
16488                     cell.title = ddaysText;
16489                     cell.className = " fc-state-disabled";
16490                 }
16491             }
16492             if(ddMatch && format){
16493                 var fvalue = d.dateFormat(format);
16494                 if(ddMatch.test(fvalue)){
16495                     cell.title = ddText.replace("%0", fvalue);
16496                     cell.className = " fc-state-disabled";
16497                 }
16498             }
16499             
16500             if (!cell.initialClassName) {
16501                 cell.initialClassName = cell.dom.className;
16502             }
16503             
16504             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16505         };
16506
16507         var i = 0;
16508         
16509         for(; i < startingPos; i++) {
16510             textEls[i].innerHTML = (++prevStart);
16511             d.setDate(d.getDate()+1);
16512             
16513             cells[i].className = "fc-past fc-other-month";
16514             setCellClass(this, cells[i]);
16515         }
16516         
16517         var intDay = 0;
16518         
16519         for(; i < days; i++){
16520             intDay = i - startingPos + 1;
16521             textEls[i].innerHTML = (intDay);
16522             d.setDate(d.getDate()+1);
16523             
16524             cells[i].className = ''; // "x-date-active";
16525             setCellClass(this, cells[i]);
16526         }
16527         var extraDays = 0;
16528         
16529         for(; i < 42; i++) {
16530             textEls[i].innerHTML = (++extraDays);
16531             d.setDate(d.getDate()+1);
16532             
16533             cells[i].className = "fc-future fc-other-month";
16534             setCellClass(this, cells[i]);
16535         }
16536         
16537         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16538         
16539         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16540         
16541         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16542         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16543         
16544         if(totalRows != 6){
16545             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16546             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16547         }
16548         
16549         this.fireEvent('monthchange', this, date);
16550         
16551         
16552         /*
16553         if(!this.internalRender){
16554             var main = this.el.dom.firstChild;
16555             var w = main.offsetWidth;
16556             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16557             Roo.fly(main).setWidth(w);
16558             this.internalRender = true;
16559             // opera does not respect the auto grow header center column
16560             // then, after it gets a width opera refuses to recalculate
16561             // without a second pass
16562             if(Roo.isOpera && !this.secondPass){
16563                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16564                 this.secondPass = true;
16565                 this.update.defer(10, this, [date]);
16566             }
16567         }
16568         */
16569         
16570     },
16571     
16572     findCell : function(dt) {
16573         dt = dt.clearTime().getTime();
16574         var ret = false;
16575         this.cells.each(function(c){
16576             //Roo.log("check " +c.dateValue + '?=' + dt);
16577             if(c.dateValue == dt){
16578                 ret = c;
16579                 return false;
16580             }
16581             return true;
16582         });
16583         
16584         return ret;
16585     },
16586     
16587     findCells : function(ev) {
16588         var s = ev.start.clone().clearTime().getTime();
16589        // Roo.log(s);
16590         var e= ev.end.clone().clearTime().getTime();
16591        // Roo.log(e);
16592         var ret = [];
16593         this.cells.each(function(c){
16594              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16595             
16596             if(c.dateValue > e){
16597                 return ;
16598             }
16599             if(c.dateValue < s){
16600                 return ;
16601             }
16602             ret.push(c);
16603         });
16604         
16605         return ret;    
16606     },
16607     
16608 //    findBestRow: function(cells)
16609 //    {
16610 //        var ret = 0;
16611 //        
16612 //        for (var i =0 ; i < cells.length;i++) {
16613 //            ret  = Math.max(cells[i].rows || 0,ret);
16614 //        }
16615 //        return ret;
16616 //        
16617 //    },
16618     
16619     
16620     addItem : function(ev)
16621     {
16622         // look for vertical location slot in
16623         var cells = this.findCells(ev);
16624         
16625 //        ev.row = this.findBestRow(cells);
16626         
16627         // work out the location.
16628         
16629         var crow = false;
16630         var rows = [];
16631         for(var i =0; i < cells.length; i++) {
16632             
16633             cells[i].row = cells[0].row;
16634             
16635             if(i == 0){
16636                 cells[i].row = cells[i].row + 1;
16637             }
16638             
16639             if (!crow) {
16640                 crow = {
16641                     start : cells[i],
16642                     end :  cells[i]
16643                 };
16644                 continue;
16645             }
16646             if (crow.start.getY() == cells[i].getY()) {
16647                 // on same row.
16648                 crow.end = cells[i];
16649                 continue;
16650             }
16651             // different row.
16652             rows.push(crow);
16653             crow = {
16654                 start: cells[i],
16655                 end : cells[i]
16656             };
16657             
16658         }
16659         
16660         rows.push(crow);
16661         ev.els = [];
16662         ev.rows = rows;
16663         ev.cells = cells;
16664         
16665         cells[0].events.push(ev);
16666         
16667         this.calevents.push(ev);
16668     },
16669     
16670     clearEvents: function() {
16671         
16672         if(!this.calevents){
16673             return;
16674         }
16675         
16676         Roo.each(this.cells.elements, function(c){
16677             c.row = 0;
16678             c.events = [];
16679             c.more = [];
16680         });
16681         
16682         Roo.each(this.calevents, function(e) {
16683             Roo.each(e.els, function(el) {
16684                 el.un('mouseenter' ,this.onEventEnter, this);
16685                 el.un('mouseleave' ,this.onEventLeave, this);
16686                 el.remove();
16687             },this);
16688         },this);
16689         
16690         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16691             e.remove();
16692         });
16693         
16694     },
16695     
16696     renderEvents: function()
16697     {   
16698         var _this = this;
16699         
16700         this.cells.each(function(c) {
16701             
16702             if(c.row < 5){
16703                 return;
16704             }
16705             
16706             var ev = c.events;
16707             
16708             var r = 4;
16709             if(c.row != c.events.length){
16710                 r = 4 - (4 - (c.row - c.events.length));
16711             }
16712             
16713             c.events = ev.slice(0, r);
16714             c.more = ev.slice(r);
16715             
16716             if(c.more.length && c.more.length == 1){
16717                 c.events.push(c.more.pop());
16718             }
16719             
16720             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16721             
16722         });
16723             
16724         this.cells.each(function(c) {
16725             
16726             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16727             
16728             
16729             for (var e = 0; e < c.events.length; e++){
16730                 var ev = c.events[e];
16731                 var rows = ev.rows;
16732                 
16733                 for(var i = 0; i < rows.length; i++) {
16734                 
16735                     // how many rows should it span..
16736
16737                     var  cfg = {
16738                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16739                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16740
16741                         unselectable : "on",
16742                         cn : [
16743                             {
16744                                 cls: 'fc-event-inner',
16745                                 cn : [
16746     //                                {
16747     //                                  tag:'span',
16748     //                                  cls: 'fc-event-time',
16749     //                                  html : cells.length > 1 ? '' : ev.time
16750     //                                },
16751                                     {
16752                                       tag:'span',
16753                                       cls: 'fc-event-title',
16754                                       html : String.format('{0}', ev.title)
16755                                     }
16756
16757
16758                                 ]
16759                             },
16760                             {
16761                                 cls: 'ui-resizable-handle ui-resizable-e',
16762                                 html : '&nbsp;&nbsp;&nbsp'
16763                             }
16764
16765                         ]
16766                     };
16767
16768                     if (i == 0) {
16769                         cfg.cls += ' fc-event-start';
16770                     }
16771                     if ((i+1) == rows.length) {
16772                         cfg.cls += ' fc-event-end';
16773                     }
16774
16775                     var ctr = _this.el.select('.fc-event-container',true).first();
16776                     var cg = ctr.createChild(cfg);
16777
16778                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16779                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16780
16781                     var r = (c.more.length) ? 1 : 0;
16782                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16783                     cg.setWidth(ebox.right - sbox.x -2);
16784
16785                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16786                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16787                     cg.on('click', _this.onEventClick, _this, ev);
16788
16789                     ev.els.push(cg);
16790                     
16791                 }
16792                 
16793             }
16794             
16795             
16796             if(c.more.length){
16797                 var  cfg = {
16798                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16799                     style : 'position: absolute',
16800                     unselectable : "on",
16801                     cn : [
16802                         {
16803                             cls: 'fc-event-inner',
16804                             cn : [
16805                                 {
16806                                   tag:'span',
16807                                   cls: 'fc-event-title',
16808                                   html : 'More'
16809                                 }
16810
16811
16812                             ]
16813                         },
16814                         {
16815                             cls: 'ui-resizable-handle ui-resizable-e',
16816                             html : '&nbsp;&nbsp;&nbsp'
16817                         }
16818
16819                     ]
16820                 };
16821
16822                 var ctr = _this.el.select('.fc-event-container',true).first();
16823                 var cg = ctr.createChild(cfg);
16824
16825                 var sbox = c.select('.fc-day-content',true).first().getBox();
16826                 var ebox = c.select('.fc-day-content',true).first().getBox();
16827                 //Roo.log(cg);
16828                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16829                 cg.setWidth(ebox.right - sbox.x -2);
16830
16831                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16832                 
16833             }
16834             
16835         });
16836         
16837         
16838         
16839     },
16840     
16841     onEventEnter: function (e, el,event,d) {
16842         this.fireEvent('evententer', this, el, event);
16843     },
16844     
16845     onEventLeave: function (e, el,event,d) {
16846         this.fireEvent('eventleave', this, el, event);
16847     },
16848     
16849     onEventClick: function (e, el,event,d) {
16850         this.fireEvent('eventclick', this, el, event);
16851     },
16852     
16853     onMonthChange: function () {
16854         this.store.load();
16855     },
16856     
16857     onMoreEventClick: function(e, el, more)
16858     {
16859         var _this = this;
16860         
16861         this.calpopover.placement = 'right';
16862         this.calpopover.setTitle('More');
16863         
16864         this.calpopover.setContent('');
16865         
16866         var ctr = this.calpopover.el.select('.popover-content', true).first();
16867         
16868         Roo.each(more, function(m){
16869             var cfg = {
16870                 cls : 'fc-event-hori fc-event-draggable',
16871                 html : m.title
16872             };
16873             var cg = ctr.createChild(cfg);
16874             
16875             cg.on('click', _this.onEventClick, _this, m);
16876         });
16877         
16878         this.calpopover.show(el);
16879         
16880         
16881     },
16882     
16883     onLoad: function () 
16884     {   
16885         this.calevents = [];
16886         var cal = this;
16887         
16888         if(this.store.getCount() > 0){
16889             this.store.data.each(function(d){
16890                cal.addItem({
16891                     id : d.data.id,
16892                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16893                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16894                     time : d.data.start_time,
16895                     title : d.data.title,
16896                     description : d.data.description,
16897                     venue : d.data.venue
16898                 });
16899             });
16900         }
16901         
16902         this.renderEvents();
16903         
16904         if(this.calevents.length && this.loadMask){
16905             this.maskEl.hide();
16906         }
16907     },
16908     
16909     onBeforeLoad: function()
16910     {
16911         this.clearEvents();
16912         if(this.loadMask){
16913             this.maskEl.show();
16914         }
16915     }
16916 });
16917
16918  
16919  /*
16920  * - LGPL
16921  *
16922  * element
16923  * 
16924  */
16925
16926 /**
16927  * @class Roo.bootstrap.Popover
16928  * @extends Roo.bootstrap.Component
16929  * Bootstrap Popover class
16930  * @cfg {String} html contents of the popover   (or false to use children..)
16931  * @cfg {String} title of popover (or false to hide)
16932  * @cfg {String} placement how it is placed
16933  * @cfg {String} trigger click || hover (or false to trigger manually)
16934  * @cfg {String} over what (parent or false to trigger manually.)
16935  * @cfg {Number} delay - delay before showing
16936  
16937  * @constructor
16938  * Create a new Popover
16939  * @param {Object} config The config object
16940  */
16941
16942 Roo.bootstrap.Popover = function(config){
16943     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16944     
16945     this.addEvents({
16946         // raw events
16947          /**
16948          * @event show
16949          * After the popover show
16950          * 
16951          * @param {Roo.bootstrap.Popover} this
16952          */
16953         "show" : true,
16954         /**
16955          * @event hide
16956          * After the popover hide
16957          * 
16958          * @param {Roo.bootstrap.Popover} this
16959          */
16960         "hide" : true
16961     });
16962 };
16963
16964 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
16965     
16966     title: 'Fill in a title',
16967     html: false,
16968     
16969     placement : 'right',
16970     trigger : 'hover', // hover
16971     
16972     delay : 0,
16973     
16974     over: 'parent',
16975     
16976     can_build_overlaid : false,
16977     
16978     getChildContainer : function()
16979     {
16980         return this.el.select('.popover-content',true).first();
16981     },
16982     
16983     getAutoCreate : function(){
16984          
16985         var cfg = {
16986            cls : 'popover roo-dynamic',
16987            style: 'display:block',
16988            cn : [
16989                 {
16990                     cls : 'arrow'
16991                 },
16992                 {
16993                     cls : 'popover-inner',
16994                     cn : [
16995                         {
16996                             tag: 'h3',
16997                             cls: 'popover-title',
16998                             html : this.title
16999                         },
17000                         {
17001                             cls : 'popover-content',
17002                             html : this.html
17003                         }
17004                     ]
17005                     
17006                 }
17007            ]
17008         };
17009         
17010         return cfg;
17011     },
17012     setTitle: function(str)
17013     {
17014         this.title = str;
17015         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17016     },
17017     setContent: function(str)
17018     {
17019         this.html = str;
17020         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17021     },
17022     // as it get's added to the bottom of the page.
17023     onRender : function(ct, position)
17024     {
17025         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17026         if(!this.el){
17027             var cfg = Roo.apply({},  this.getAutoCreate());
17028             cfg.id = Roo.id();
17029             
17030             if (this.cls) {
17031                 cfg.cls += ' ' + this.cls;
17032             }
17033             if (this.style) {
17034                 cfg.style = this.style;
17035             }
17036             //Roo.log("adding to ");
17037             this.el = Roo.get(document.body).createChild(cfg, position);
17038 //            Roo.log(this.el);
17039         }
17040         this.initEvents();
17041     },
17042     
17043     initEvents : function()
17044     {
17045         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17046         this.el.enableDisplayMode('block');
17047         this.el.hide();
17048         if (this.over === false) {
17049             return; 
17050         }
17051         if (this.triggers === false) {
17052             return;
17053         }
17054         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17055         var triggers = this.trigger ? this.trigger.split(' ') : [];
17056         Roo.each(triggers, function(trigger) {
17057         
17058             if (trigger == 'click') {
17059                 on_el.on('click', this.toggle, this);
17060             } else if (trigger != 'manual') {
17061                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17062                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17063       
17064                 on_el.on(eventIn  ,this.enter, this);
17065                 on_el.on(eventOut, this.leave, this);
17066             }
17067         }, this);
17068         
17069     },
17070     
17071     
17072     // private
17073     timeout : null,
17074     hoverState : null,
17075     
17076     toggle : function () {
17077         this.hoverState == 'in' ? this.leave() : this.enter();
17078     },
17079     
17080     enter : function () {
17081         
17082         clearTimeout(this.timeout);
17083     
17084         this.hoverState = 'in';
17085     
17086         if (!this.delay || !this.delay.show) {
17087             this.show();
17088             return;
17089         }
17090         var _t = this;
17091         this.timeout = setTimeout(function () {
17092             if (_t.hoverState == 'in') {
17093                 _t.show();
17094             }
17095         }, this.delay.show)
17096     },
17097     
17098     leave : function() {
17099         clearTimeout(this.timeout);
17100     
17101         this.hoverState = 'out';
17102     
17103         if (!this.delay || !this.delay.hide) {
17104             this.hide();
17105             return;
17106         }
17107         var _t = this;
17108         this.timeout = setTimeout(function () {
17109             if (_t.hoverState == 'out') {
17110                 _t.hide();
17111             }
17112         }, this.delay.hide)
17113     },
17114     
17115     show : function (on_el)
17116     {
17117         if (!on_el) {
17118             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17119         }
17120         
17121         // set content.
17122         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17123         if (this.html !== false) {
17124             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17125         }
17126         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17127         if (!this.title.length) {
17128             this.el.select('.popover-title',true).hide();
17129         }
17130         
17131         var placement = typeof this.placement == 'function' ?
17132             this.placement.call(this, this.el, on_el) :
17133             this.placement;
17134             
17135         var autoToken = /\s?auto?\s?/i;
17136         var autoPlace = autoToken.test(placement);
17137         if (autoPlace) {
17138             placement = placement.replace(autoToken, '') || 'top';
17139         }
17140         
17141         //this.el.detach()
17142         //this.el.setXY([0,0]);
17143         this.el.show();
17144         this.el.dom.style.display='block';
17145         this.el.addClass(placement);
17146         
17147         //this.el.appendTo(on_el);
17148         
17149         var p = this.getPosition();
17150         var box = this.el.getBox();
17151         
17152         if (autoPlace) {
17153             // fixme..
17154         }
17155         var align = Roo.bootstrap.Popover.alignment[placement];
17156         this.el.alignTo(on_el, align[0],align[1]);
17157         //var arrow = this.el.select('.arrow',true).first();
17158         //arrow.set(align[2], 
17159         
17160         this.el.addClass('in');
17161         
17162         
17163         if (this.el.hasClass('fade')) {
17164             // fade it?
17165         }
17166         
17167         this.hoverState = 'in';
17168         
17169         this.fireEvent('show', this);
17170         
17171     },
17172     hide : function()
17173     {
17174         this.el.setXY([0,0]);
17175         this.el.removeClass('in');
17176         this.el.hide();
17177         this.hoverState = null;
17178         
17179         this.fireEvent('hide', this);
17180     }
17181     
17182 });
17183
17184 Roo.bootstrap.Popover.alignment = {
17185     'left' : ['r-l', [-10,0], 'right'],
17186     'right' : ['l-r', [10,0], 'left'],
17187     'bottom' : ['t-b', [0,10], 'top'],
17188     'top' : [ 'b-t', [0,-10], 'bottom']
17189 };
17190
17191  /*
17192  * - LGPL
17193  *
17194  * Progress
17195  * 
17196  */
17197
17198 /**
17199  * @class Roo.bootstrap.Progress
17200  * @extends Roo.bootstrap.Component
17201  * Bootstrap Progress class
17202  * @cfg {Boolean} striped striped of the progress bar
17203  * @cfg {Boolean} active animated of the progress bar
17204  * 
17205  * 
17206  * @constructor
17207  * Create a new Progress
17208  * @param {Object} config The config object
17209  */
17210
17211 Roo.bootstrap.Progress = function(config){
17212     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17213 };
17214
17215 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17216     
17217     striped : false,
17218     active: false,
17219     
17220     getAutoCreate : function(){
17221         var cfg = {
17222             tag: 'div',
17223             cls: 'progress'
17224         };
17225         
17226         
17227         if(this.striped){
17228             cfg.cls += ' progress-striped';
17229         }
17230       
17231         if(this.active){
17232             cfg.cls += ' active';
17233         }
17234         
17235         
17236         return cfg;
17237     }
17238    
17239 });
17240
17241  
17242
17243  /*
17244  * - LGPL
17245  *
17246  * ProgressBar
17247  * 
17248  */
17249
17250 /**
17251  * @class Roo.bootstrap.ProgressBar
17252  * @extends Roo.bootstrap.Component
17253  * Bootstrap ProgressBar class
17254  * @cfg {Number} aria_valuenow aria-value now
17255  * @cfg {Number} aria_valuemin aria-value min
17256  * @cfg {Number} aria_valuemax aria-value max
17257  * @cfg {String} label label for the progress bar
17258  * @cfg {String} panel (success | info | warning | danger )
17259  * @cfg {String} role role of the progress bar
17260  * @cfg {String} sr_only text
17261  * 
17262  * 
17263  * @constructor
17264  * Create a new ProgressBar
17265  * @param {Object} config The config object
17266  */
17267
17268 Roo.bootstrap.ProgressBar = function(config){
17269     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17270 };
17271
17272 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17273     
17274     aria_valuenow : 0,
17275     aria_valuemin : 0,
17276     aria_valuemax : 100,
17277     label : false,
17278     panel : false,
17279     role : false,
17280     sr_only: false,
17281     
17282     getAutoCreate : function()
17283     {
17284         
17285         var cfg = {
17286             tag: 'div',
17287             cls: 'progress-bar',
17288             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17289         };
17290         
17291         if(this.sr_only){
17292             cfg.cn = {
17293                 tag: 'span',
17294                 cls: 'sr-only',
17295                 html: this.sr_only
17296             }
17297         }
17298         
17299         if(this.role){
17300             cfg.role = this.role;
17301         }
17302         
17303         if(this.aria_valuenow){
17304             cfg['aria-valuenow'] = this.aria_valuenow;
17305         }
17306         
17307         if(this.aria_valuemin){
17308             cfg['aria-valuemin'] = this.aria_valuemin;
17309         }
17310         
17311         if(this.aria_valuemax){
17312             cfg['aria-valuemax'] = this.aria_valuemax;
17313         }
17314         
17315         if(this.label && !this.sr_only){
17316             cfg.html = this.label;
17317         }
17318         
17319         if(this.panel){
17320             cfg.cls += ' progress-bar-' + this.panel;
17321         }
17322         
17323         return cfg;
17324     },
17325     
17326     update : function(aria_valuenow)
17327     {
17328         this.aria_valuenow = aria_valuenow;
17329         
17330         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17331     }
17332    
17333 });
17334
17335  
17336
17337  /*
17338  * - LGPL
17339  *
17340  * column
17341  * 
17342  */
17343
17344 /**
17345  * @class Roo.bootstrap.TabGroup
17346  * @extends Roo.bootstrap.Column
17347  * Bootstrap Column class
17348  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17349  * @cfg {Boolean} carousel true to make the group behave like a carousel
17350  * @cfg {Boolean} bullets show bullets for the panels
17351  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17352  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17353  * @cfg {Boolean} showarrow (true|false) show arrow default true
17354  * 
17355  * @constructor
17356  * Create a new TabGroup
17357  * @param {Object} config The config object
17358  */
17359
17360 Roo.bootstrap.TabGroup = function(config){
17361     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17362     if (!this.navId) {
17363         this.navId = Roo.id();
17364     }
17365     this.tabs = [];
17366     Roo.bootstrap.TabGroup.register(this);
17367     
17368 };
17369
17370 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17371     
17372     carousel : false,
17373     transition : false,
17374     bullets : 0,
17375     timer : 0,
17376     autoslide : false,
17377     slideFn : false,
17378     slideOnTouch : false,
17379     showarrow : true,
17380     
17381     getAutoCreate : function()
17382     {
17383         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17384         
17385         cfg.cls += ' tab-content';
17386         
17387         if (this.carousel) {
17388             cfg.cls += ' carousel slide';
17389             
17390             cfg.cn = [{
17391                cls : 'carousel-inner',
17392                cn : []
17393             }];
17394         
17395             if(this.bullets  && !Roo.isTouch){
17396                 
17397                 var bullets = {
17398                     cls : 'carousel-bullets',
17399                     cn : []
17400                 };
17401                
17402                 if(this.bullets_cls){
17403                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17404                 }
17405                 
17406                 bullets.cn.push({
17407                     cls : 'clear'
17408                 });
17409                 
17410                 cfg.cn[0].cn.push(bullets);
17411             }
17412             
17413             if(this.showarrow){
17414                 cfg.cn[0].cn.push({
17415                     tag : 'div',
17416                     class : 'carousel-arrow',
17417                     cn : [
17418                         {
17419                             tag : 'div',
17420                             class : 'carousel-prev',
17421                             cn : [
17422                                 {
17423                                     tag : 'i',
17424                                     class : 'fa fa-chevron-left'
17425                                 }
17426                             ]
17427                         },
17428                         {
17429                             tag : 'div',
17430                             class : 'carousel-next',
17431                             cn : [
17432                                 {
17433                                     tag : 'i',
17434                                     class : 'fa fa-chevron-right'
17435                                 }
17436                             ]
17437                         }
17438                     ]
17439                 });
17440             }
17441             
17442         }
17443         
17444         return cfg;
17445     },
17446     
17447     initEvents:  function()
17448     {
17449 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17450 //            this.el.on("touchstart", this.onTouchStart, this);
17451 //        }
17452         
17453         if(this.autoslide){
17454             var _this = this;
17455             
17456             this.slideFn = window.setInterval(function() {
17457                 _this.showPanelNext();
17458             }, this.timer);
17459         }
17460         
17461         if(this.showarrow){
17462             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17463             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17464         }
17465         
17466         
17467     },
17468     
17469 //    onTouchStart : function(e, el, o)
17470 //    {
17471 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17472 //            return;
17473 //        }
17474 //        
17475 //        this.showPanelNext();
17476 //    },
17477     
17478     
17479     getChildContainer : function()
17480     {
17481         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17482     },
17483     
17484     /**
17485     * register a Navigation item
17486     * @param {Roo.bootstrap.NavItem} the navitem to add
17487     */
17488     register : function(item)
17489     {
17490         this.tabs.push( item);
17491         item.navId = this.navId; // not really needed..
17492         this.addBullet();
17493     
17494     },
17495     
17496     getActivePanel : function()
17497     {
17498         var r = false;
17499         Roo.each(this.tabs, function(t) {
17500             if (t.active) {
17501                 r = t;
17502                 return false;
17503             }
17504             return null;
17505         });
17506         return r;
17507         
17508     },
17509     getPanelByName : function(n)
17510     {
17511         var r = false;
17512         Roo.each(this.tabs, function(t) {
17513             if (t.tabId == n) {
17514                 r = t;
17515                 return false;
17516             }
17517             return null;
17518         });
17519         return r;
17520     },
17521     indexOfPanel : function(p)
17522     {
17523         var r = false;
17524         Roo.each(this.tabs, function(t,i) {
17525             if (t.tabId == p.tabId) {
17526                 r = i;
17527                 return false;
17528             }
17529             return null;
17530         });
17531         return r;
17532     },
17533     /**
17534      * show a specific panel
17535      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17536      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17537      */
17538     showPanel : function (pan)
17539     {
17540         if(this.transition || typeof(pan) == 'undefined'){
17541             Roo.log("waiting for the transitionend");
17542             return;
17543         }
17544         
17545         if (typeof(pan) == 'number') {
17546             pan = this.tabs[pan];
17547         }
17548         
17549         if (typeof(pan) == 'string') {
17550             pan = this.getPanelByName(pan);
17551         }
17552         
17553         var cur = this.getActivePanel();
17554         
17555         if(!pan || !cur){
17556             Roo.log('pan or acitve pan is undefined');
17557             return false;
17558         }
17559         
17560         if (pan.tabId == this.getActivePanel().tabId) {
17561             return true;
17562         }
17563         
17564         if (false === cur.fireEvent('beforedeactivate')) {
17565             return false;
17566         }
17567         
17568         if(this.bullets > 0 && !Roo.isTouch){
17569             this.setActiveBullet(this.indexOfPanel(pan));
17570         }
17571         
17572         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17573             
17574             this.transition = true;
17575             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17576             var lr = dir == 'next' ? 'left' : 'right';
17577             pan.el.addClass(dir); // or prev
17578             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17579             cur.el.addClass(lr); // or right
17580             pan.el.addClass(lr);
17581             
17582             var _this = this;
17583             cur.el.on('transitionend', function() {
17584                 Roo.log("trans end?");
17585                 
17586                 pan.el.removeClass([lr,dir]);
17587                 pan.setActive(true);
17588                 
17589                 cur.el.removeClass([lr]);
17590                 cur.setActive(false);
17591                 
17592                 _this.transition = false;
17593                 
17594             }, this, { single:  true } );
17595             
17596             return true;
17597         }
17598         
17599         cur.setActive(false);
17600         pan.setActive(true);
17601         
17602         return true;
17603         
17604     },
17605     showPanelNext : function()
17606     {
17607         var i = this.indexOfPanel(this.getActivePanel());
17608         
17609         if (i >= this.tabs.length - 1 && !this.autoslide) {
17610             return;
17611         }
17612         
17613         if (i >= this.tabs.length - 1 && this.autoslide) {
17614             i = -1;
17615         }
17616         
17617         this.showPanel(this.tabs[i+1]);
17618     },
17619     
17620     showPanelPrev : function()
17621     {
17622         var i = this.indexOfPanel(this.getActivePanel());
17623         
17624         if (i  < 1 && !this.autoslide) {
17625             return;
17626         }
17627         
17628         if (i < 1 && this.autoslide) {
17629             i = this.tabs.length;
17630         }
17631         
17632         this.showPanel(this.tabs[i-1]);
17633     },
17634     
17635     
17636     addBullet: function()
17637     {
17638         if(!this.bullets || Roo.isTouch){
17639             return;
17640         }
17641         var ctr = this.el.select('.carousel-bullets',true).first();
17642         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17643         var bullet = ctr.createChild({
17644             cls : 'bullet bullet-' + i
17645         },ctr.dom.lastChild);
17646         
17647         
17648         var _this = this;
17649         
17650         bullet.on('click', (function(e, el, o, ii, t){
17651
17652             e.preventDefault();
17653
17654             this.showPanel(ii);
17655
17656             if(this.autoslide && this.slideFn){
17657                 clearInterval(this.slideFn);
17658                 this.slideFn = window.setInterval(function() {
17659                     _this.showPanelNext();
17660                 }, this.timer);
17661             }
17662
17663         }).createDelegate(this, [i, bullet], true));
17664                 
17665         
17666     },
17667      
17668     setActiveBullet : function(i)
17669     {
17670         if(Roo.isTouch){
17671             return;
17672         }
17673         
17674         Roo.each(this.el.select('.bullet', true).elements, function(el){
17675             el.removeClass('selected');
17676         });
17677
17678         var bullet = this.el.select('.bullet-' + i, true).first();
17679         
17680         if(!bullet){
17681             return;
17682         }
17683         
17684         bullet.addClass('selected');
17685     }
17686     
17687     
17688   
17689 });
17690
17691  
17692
17693  
17694  
17695 Roo.apply(Roo.bootstrap.TabGroup, {
17696     
17697     groups: {},
17698      /**
17699     * register a Navigation Group
17700     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17701     */
17702     register : function(navgrp)
17703     {
17704         this.groups[navgrp.navId] = navgrp;
17705         
17706     },
17707     /**
17708     * fetch a Navigation Group based on the navigation ID
17709     * if one does not exist , it will get created.
17710     * @param {string} the navgroup to add
17711     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17712     */
17713     get: function(navId) {
17714         if (typeof(this.groups[navId]) == 'undefined') {
17715             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17716         }
17717         return this.groups[navId] ;
17718     }
17719     
17720     
17721     
17722 });
17723
17724  /*
17725  * - LGPL
17726  *
17727  * TabPanel
17728  * 
17729  */
17730
17731 /**
17732  * @class Roo.bootstrap.TabPanel
17733  * @extends Roo.bootstrap.Component
17734  * Bootstrap TabPanel class
17735  * @cfg {Boolean} active panel active
17736  * @cfg {String} html panel content
17737  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17738  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17739  * @cfg {String} href click to link..
17740  * 
17741  * 
17742  * @constructor
17743  * Create a new TabPanel
17744  * @param {Object} config The config object
17745  */
17746
17747 Roo.bootstrap.TabPanel = function(config){
17748     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17749     this.addEvents({
17750         /**
17751              * @event changed
17752              * Fires when the active status changes
17753              * @param {Roo.bootstrap.TabPanel} this
17754              * @param {Boolean} state the new state
17755             
17756          */
17757         'changed': true,
17758         /**
17759              * @event beforedeactivate
17760              * Fires before a tab is de-activated - can be used to do validation on a form.
17761              * @param {Roo.bootstrap.TabPanel} this
17762              * @return {Boolean} false if there is an error
17763             
17764          */
17765         'beforedeactivate': true
17766      });
17767     
17768     this.tabId = this.tabId || Roo.id();
17769   
17770 };
17771
17772 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17773     
17774     active: false,
17775     html: false,
17776     tabId: false,
17777     navId : false,
17778     href : '',
17779     
17780     getAutoCreate : function(){
17781         var cfg = {
17782             tag: 'div',
17783             // item is needed for carousel - not sure if it has any effect otherwise
17784             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17785             html: this.html || ''
17786         };
17787         
17788         if(this.active){
17789             cfg.cls += ' active';
17790         }
17791         
17792         if(this.tabId){
17793             cfg.tabId = this.tabId;
17794         }
17795         
17796         
17797         return cfg;
17798     },
17799     
17800     initEvents:  function()
17801     {
17802         var p = this.parent();
17803         
17804         this.navId = this.navId || p.navId;
17805         
17806         if (typeof(this.navId) != 'undefined') {
17807             // not really needed.. but just in case.. parent should be a NavGroup.
17808             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17809             
17810             tg.register(this);
17811             
17812             var i = tg.tabs.length - 1;
17813             
17814             if(this.active && tg.bullets > 0 && i < tg.bullets){
17815                 tg.setActiveBullet(i);
17816             }
17817         }
17818         
17819         this.el.on('click', this.onClick, this);
17820         
17821         if(Roo.isTouch){
17822             this.el.on("touchstart", this.onTouchStart, this);
17823             this.el.on("touchmove", this.onTouchMove, this);
17824             this.el.on("touchend", this.onTouchEnd, this);
17825         }
17826         
17827     },
17828     
17829     onRender : function(ct, position)
17830     {
17831         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17832     },
17833     
17834     setActive : function(state)
17835     {
17836         Roo.log("panel - set active " + this.tabId + "=" + state);
17837         
17838         this.active = state;
17839         if (!state) {
17840             this.el.removeClass('active');
17841             
17842         } else  if (!this.el.hasClass('active')) {
17843             this.el.addClass('active');
17844         }
17845         
17846         this.fireEvent('changed', this, state);
17847     },
17848     
17849     onClick : function(e)
17850     {
17851         e.preventDefault();
17852         
17853         if(!this.href.length){
17854             return;
17855         }
17856         
17857         window.location.href = this.href;
17858     },
17859     
17860     startX : 0,
17861     startY : 0,
17862     endX : 0,
17863     endY : 0,
17864     swiping : false,
17865     
17866     onTouchStart : function(e)
17867     {
17868         this.swiping = false;
17869         
17870         this.startX = e.browserEvent.touches[0].clientX;
17871         this.startY = e.browserEvent.touches[0].clientY;
17872     },
17873     
17874     onTouchMove : function(e)
17875     {
17876         this.swiping = true;
17877         
17878         this.endX = e.browserEvent.touches[0].clientX;
17879         this.endY = e.browserEvent.touches[0].clientY;
17880     },
17881     
17882     onTouchEnd : function(e)
17883     {
17884         if(!this.swiping){
17885             this.onClick(e);
17886             return;
17887         }
17888         
17889         var tabGroup = this.parent();
17890         
17891         if(this.endX > this.startX){ // swiping right
17892             tabGroup.showPanelPrev();
17893             return;
17894         }
17895         
17896         if(this.startX > this.endX){ // swiping left
17897             tabGroup.showPanelNext();
17898             return;
17899         }
17900     }
17901     
17902     
17903 });
17904  
17905
17906  
17907
17908  /*
17909  * - LGPL
17910  *
17911  * DateField
17912  * 
17913  */
17914
17915 /**
17916  * @class Roo.bootstrap.DateField
17917  * @extends Roo.bootstrap.Input
17918  * Bootstrap DateField class
17919  * @cfg {Number} weekStart default 0
17920  * @cfg {String} viewMode default empty, (months|years)
17921  * @cfg {String} minViewMode default empty, (months|years)
17922  * @cfg {Number} startDate default -Infinity
17923  * @cfg {Number} endDate default Infinity
17924  * @cfg {Boolean} todayHighlight default false
17925  * @cfg {Boolean} todayBtn default false
17926  * @cfg {Boolean} calendarWeeks default false
17927  * @cfg {Object} daysOfWeekDisabled default empty
17928  * @cfg {Boolean} singleMode default false (true | false)
17929  * 
17930  * @cfg {Boolean} keyboardNavigation default true
17931  * @cfg {String} language default en
17932  * 
17933  * @constructor
17934  * Create a new DateField
17935  * @param {Object} config The config object
17936  */
17937
17938 Roo.bootstrap.DateField = function(config){
17939     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17940      this.addEvents({
17941             /**
17942              * @event show
17943              * Fires when this field show.
17944              * @param {Roo.bootstrap.DateField} this
17945              * @param {Mixed} date The date value
17946              */
17947             show : true,
17948             /**
17949              * @event show
17950              * Fires when this field hide.
17951              * @param {Roo.bootstrap.DateField} this
17952              * @param {Mixed} date The date value
17953              */
17954             hide : true,
17955             /**
17956              * @event select
17957              * Fires when select a date.
17958              * @param {Roo.bootstrap.DateField} this
17959              * @param {Mixed} date The date value
17960              */
17961             select : true,
17962             /**
17963              * @event beforeselect
17964              * Fires when before select a date.
17965              * @param {Roo.bootstrap.DateField} this
17966              * @param {Mixed} date The date value
17967              */
17968             beforeselect : true
17969         });
17970 };
17971
17972 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
17973     
17974     /**
17975      * @cfg {String} format
17976      * The default date format string which can be overriden for localization support.  The format must be
17977      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17978      */
17979     format : "m/d/y",
17980     /**
17981      * @cfg {String} altFormats
17982      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17983      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17984      */
17985     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17986     
17987     weekStart : 0,
17988     
17989     viewMode : '',
17990     
17991     minViewMode : '',
17992     
17993     todayHighlight : false,
17994     
17995     todayBtn: false,
17996     
17997     language: 'en',
17998     
17999     keyboardNavigation: true,
18000     
18001     calendarWeeks: false,
18002     
18003     startDate: -Infinity,
18004     
18005     endDate: Infinity,
18006     
18007     daysOfWeekDisabled: [],
18008     
18009     _events: [],
18010     
18011     singleMode : false,
18012     
18013     UTCDate: function()
18014     {
18015         return new Date(Date.UTC.apply(Date, arguments));
18016     },
18017     
18018     UTCToday: function()
18019     {
18020         var today = new Date();
18021         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18022     },
18023     
18024     getDate: function() {
18025             var d = this.getUTCDate();
18026             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18027     },
18028     
18029     getUTCDate: function() {
18030             return this.date;
18031     },
18032     
18033     setDate: function(d) {
18034             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18035     },
18036     
18037     setUTCDate: function(d) {
18038             this.date = d;
18039             this.setValue(this.formatDate(this.date));
18040     },
18041         
18042     onRender: function(ct, position)
18043     {
18044         
18045         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18046         
18047         this.language = this.language || 'en';
18048         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18049         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18050         
18051         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18052         this.format = this.format || 'm/d/y';
18053         this.isInline = false;
18054         this.isInput = true;
18055         this.component = this.el.select('.add-on', true).first() || false;
18056         this.component = (this.component && this.component.length === 0) ? false : this.component;
18057         this.hasInput = this.component && this.inputEl().length;
18058         
18059         if (typeof(this.minViewMode === 'string')) {
18060             switch (this.minViewMode) {
18061                 case 'months':
18062                     this.minViewMode = 1;
18063                     break;
18064                 case 'years':
18065                     this.minViewMode = 2;
18066                     break;
18067                 default:
18068                     this.minViewMode = 0;
18069                     break;
18070             }
18071         }
18072         
18073         if (typeof(this.viewMode === 'string')) {
18074             switch (this.viewMode) {
18075                 case 'months':
18076                     this.viewMode = 1;
18077                     break;
18078                 case 'years':
18079                     this.viewMode = 2;
18080                     break;
18081                 default:
18082                     this.viewMode = 0;
18083                     break;
18084             }
18085         }
18086                 
18087         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18088         
18089 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18090         
18091         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18092         
18093         this.picker().on('mousedown', this.onMousedown, this);
18094         this.picker().on('click', this.onClick, this);
18095         
18096         this.picker().addClass('datepicker-dropdown');
18097         
18098         this.startViewMode = this.viewMode;
18099         
18100         if(this.singleMode){
18101             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18102                 v.setVisibilityMode(Roo.Element.DISPLAY);
18103                 v.hide();
18104             });
18105             
18106             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18107                 v.setStyle('width', '189px');
18108             });
18109         }
18110         
18111         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18112             if(!this.calendarWeeks){
18113                 v.remove();
18114                 return;
18115             }
18116             
18117             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18118             v.attr('colspan', function(i, val){
18119                 return parseInt(val) + 1;
18120             });
18121         });
18122                         
18123         
18124         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18125         
18126         this.setStartDate(this.startDate);
18127         this.setEndDate(this.endDate);
18128         
18129         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18130         
18131         this.fillDow();
18132         this.fillMonths();
18133         this.update();
18134         this.showMode();
18135         
18136         if(this.isInline) {
18137             this.show();
18138         }
18139     },
18140     
18141     picker : function()
18142     {
18143         return this.pickerEl;
18144 //        return this.el.select('.datepicker', true).first();
18145     },
18146     
18147     fillDow: function()
18148     {
18149         var dowCnt = this.weekStart;
18150         
18151         var dow = {
18152             tag: 'tr',
18153             cn: [
18154                 
18155             ]
18156         };
18157         
18158         if(this.calendarWeeks){
18159             dow.cn.push({
18160                 tag: 'th',
18161                 cls: 'cw',
18162                 html: '&nbsp;'
18163             })
18164         }
18165         
18166         while (dowCnt < this.weekStart + 7) {
18167             dow.cn.push({
18168                 tag: 'th',
18169                 cls: 'dow',
18170                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18171             });
18172         }
18173         
18174         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18175     },
18176     
18177     fillMonths: function()
18178     {    
18179         var i = 0;
18180         var months = this.picker().select('>.datepicker-months td', true).first();
18181         
18182         months.dom.innerHTML = '';
18183         
18184         while (i < 12) {
18185             var month = {
18186                 tag: 'span',
18187                 cls: 'month',
18188                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18189             };
18190             
18191             months.createChild(month);
18192         }
18193         
18194     },
18195     
18196     update: function()
18197     {
18198         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;
18199         
18200         if (this.date < this.startDate) {
18201             this.viewDate = new Date(this.startDate);
18202         } else if (this.date > this.endDate) {
18203             this.viewDate = new Date(this.endDate);
18204         } else {
18205             this.viewDate = new Date(this.date);
18206         }
18207         
18208         this.fill();
18209     },
18210     
18211     fill: function() 
18212     {
18213         var d = new Date(this.viewDate),
18214                 year = d.getUTCFullYear(),
18215                 month = d.getUTCMonth(),
18216                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18217                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18218                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18219                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18220                 currentDate = this.date && this.date.valueOf(),
18221                 today = this.UTCToday();
18222         
18223         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18224         
18225 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18226         
18227 //        this.picker.select('>tfoot th.today').
18228 //                                              .text(dates[this.language].today)
18229 //                                              .toggle(this.todayBtn !== false);
18230     
18231         this.updateNavArrows();
18232         this.fillMonths();
18233                                                 
18234         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18235         
18236         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18237          
18238         prevMonth.setUTCDate(day);
18239         
18240         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18241         
18242         var nextMonth = new Date(prevMonth);
18243         
18244         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18245         
18246         nextMonth = nextMonth.valueOf();
18247         
18248         var fillMonths = false;
18249         
18250         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18251         
18252         while(prevMonth.valueOf() < nextMonth) {
18253             var clsName = '';
18254             
18255             if (prevMonth.getUTCDay() === this.weekStart) {
18256                 if(fillMonths){
18257                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18258                 }
18259                     
18260                 fillMonths = {
18261                     tag: 'tr',
18262                     cn: []
18263                 };
18264                 
18265                 if(this.calendarWeeks){
18266                     // ISO 8601: First week contains first thursday.
18267                     // ISO also states week starts on Monday, but we can be more abstract here.
18268                     var
18269                     // Start of current week: based on weekstart/current date
18270                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18271                     // Thursday of this week
18272                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18273                     // First Thursday of year, year from thursday
18274                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18275                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18276                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18277                     
18278                     fillMonths.cn.push({
18279                         tag: 'td',
18280                         cls: 'cw',
18281                         html: calWeek
18282                     });
18283                 }
18284             }
18285             
18286             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18287                 clsName += ' old';
18288             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18289                 clsName += ' new';
18290             }
18291             if (this.todayHighlight &&
18292                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18293                 prevMonth.getUTCMonth() == today.getMonth() &&
18294                 prevMonth.getUTCDate() == today.getDate()) {
18295                 clsName += ' today';
18296             }
18297             
18298             if (currentDate && prevMonth.valueOf() === currentDate) {
18299                 clsName += ' active';
18300             }
18301             
18302             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18303                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18304                     clsName += ' disabled';
18305             }
18306             
18307             fillMonths.cn.push({
18308                 tag: 'td',
18309                 cls: 'day ' + clsName,
18310                 html: prevMonth.getDate()
18311             });
18312             
18313             prevMonth.setDate(prevMonth.getDate()+1);
18314         }
18315           
18316         var currentYear = this.date && this.date.getUTCFullYear();
18317         var currentMonth = this.date && this.date.getUTCMonth();
18318         
18319         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18320         
18321         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18322             v.removeClass('active');
18323             
18324             if(currentYear === year && k === currentMonth){
18325                 v.addClass('active');
18326             }
18327             
18328             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18329                 v.addClass('disabled');
18330             }
18331             
18332         });
18333         
18334         
18335         year = parseInt(year/10, 10) * 10;
18336         
18337         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18338         
18339         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18340         
18341         year -= 1;
18342         for (var i = -1; i < 11; i++) {
18343             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18344                 tag: 'span',
18345                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18346                 html: year
18347             });
18348             
18349             year += 1;
18350         }
18351     },
18352     
18353     showMode: function(dir) 
18354     {
18355         if (dir) {
18356             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18357         }
18358         
18359         Roo.each(this.picker().select('>div',true).elements, function(v){
18360             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18361             v.hide();
18362         });
18363         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18364     },
18365     
18366     place: function()
18367     {
18368         if(this.isInline) {
18369             return;
18370         }
18371         
18372         this.picker().removeClass(['bottom', 'top']);
18373         
18374         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18375             /*
18376              * place to the top of element!
18377              *
18378              */
18379             
18380             this.picker().addClass('top');
18381             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18382             
18383             return;
18384         }
18385         
18386         this.picker().addClass('bottom');
18387         
18388         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18389     },
18390     
18391     parseDate : function(value)
18392     {
18393         if(!value || value instanceof Date){
18394             return value;
18395         }
18396         var v = Date.parseDate(value, this.format);
18397         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18398             v = Date.parseDate(value, 'Y-m-d');
18399         }
18400         if(!v && this.altFormats){
18401             if(!this.altFormatsArray){
18402                 this.altFormatsArray = this.altFormats.split("|");
18403             }
18404             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18405                 v = Date.parseDate(value, this.altFormatsArray[i]);
18406             }
18407         }
18408         return v;
18409     },
18410     
18411     formatDate : function(date, fmt)
18412     {   
18413         return (!date || !(date instanceof Date)) ?
18414         date : date.dateFormat(fmt || this.format);
18415     },
18416     
18417     onFocus : function()
18418     {
18419         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18420         this.show();
18421     },
18422     
18423     onBlur : function()
18424     {
18425         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18426         
18427         var d = this.inputEl().getValue();
18428         
18429         this.setValue(d);
18430                 
18431         this.hide();
18432     },
18433     
18434     show : function()
18435     {
18436         this.picker().show();
18437         this.update();
18438         this.place();
18439         
18440         this.fireEvent('show', this, this.date);
18441     },
18442     
18443     hide : function()
18444     {
18445         if(this.isInline) {
18446             return;
18447         }
18448         this.picker().hide();
18449         this.viewMode = this.startViewMode;
18450         this.showMode();
18451         
18452         this.fireEvent('hide', this, this.date);
18453         
18454     },
18455     
18456     onMousedown: function(e)
18457     {
18458         e.stopPropagation();
18459         e.preventDefault();
18460     },
18461     
18462     keyup: function(e)
18463     {
18464         Roo.bootstrap.DateField.superclass.keyup.call(this);
18465         this.update();
18466     },
18467
18468     setValue: function(v)
18469     {
18470         if(this.fireEvent('beforeselect', this, v) !== false){
18471             var d = new Date(this.parseDate(v) ).clearTime();
18472         
18473             if(isNaN(d.getTime())){
18474                 this.date = this.viewDate = '';
18475                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18476                 return;
18477             }
18478
18479             v = this.formatDate(d);
18480
18481             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18482
18483             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18484
18485             this.update();
18486
18487             this.fireEvent('select', this, this.date);
18488         }
18489     },
18490     
18491     getValue: function()
18492     {
18493         return this.formatDate(this.date);
18494     },
18495     
18496     fireKey: function(e)
18497     {
18498         if (!this.picker().isVisible()){
18499             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18500                 this.show();
18501             }
18502             return;
18503         }
18504         
18505         var dateChanged = false,
18506         dir, day, month,
18507         newDate, newViewDate;
18508         
18509         switch(e.keyCode){
18510             case 27: // escape
18511                 this.hide();
18512                 e.preventDefault();
18513                 break;
18514             case 37: // left
18515             case 39: // right
18516                 if (!this.keyboardNavigation) {
18517                     break;
18518                 }
18519                 dir = e.keyCode == 37 ? -1 : 1;
18520                 
18521                 if (e.ctrlKey){
18522                     newDate = this.moveYear(this.date, dir);
18523                     newViewDate = this.moveYear(this.viewDate, dir);
18524                 } else if (e.shiftKey){
18525                     newDate = this.moveMonth(this.date, dir);
18526                     newViewDate = this.moveMonth(this.viewDate, dir);
18527                 } else {
18528                     newDate = new Date(this.date);
18529                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18530                     newViewDate = new Date(this.viewDate);
18531                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18532                 }
18533                 if (this.dateWithinRange(newDate)){
18534                     this.date = newDate;
18535                     this.viewDate = newViewDate;
18536                     this.setValue(this.formatDate(this.date));
18537 //                    this.update();
18538                     e.preventDefault();
18539                     dateChanged = true;
18540                 }
18541                 break;
18542             case 38: // up
18543             case 40: // down
18544                 if (!this.keyboardNavigation) {
18545                     break;
18546                 }
18547                 dir = e.keyCode == 38 ? -1 : 1;
18548                 if (e.ctrlKey){
18549                     newDate = this.moveYear(this.date, dir);
18550                     newViewDate = this.moveYear(this.viewDate, dir);
18551                 } else if (e.shiftKey){
18552                     newDate = this.moveMonth(this.date, dir);
18553                     newViewDate = this.moveMonth(this.viewDate, dir);
18554                 } else {
18555                     newDate = new Date(this.date);
18556                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18557                     newViewDate = new Date(this.viewDate);
18558                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18559                 }
18560                 if (this.dateWithinRange(newDate)){
18561                     this.date = newDate;
18562                     this.viewDate = newViewDate;
18563                     this.setValue(this.formatDate(this.date));
18564 //                    this.update();
18565                     e.preventDefault();
18566                     dateChanged = true;
18567                 }
18568                 break;
18569             case 13: // enter
18570                 this.setValue(this.formatDate(this.date));
18571                 this.hide();
18572                 e.preventDefault();
18573                 break;
18574             case 9: // tab
18575                 this.setValue(this.formatDate(this.date));
18576                 this.hide();
18577                 break;
18578             case 16: // shift
18579             case 17: // ctrl
18580             case 18: // alt
18581                 break;
18582             default :
18583                 this.hide();
18584                 
18585         }
18586     },
18587     
18588     
18589     onClick: function(e) 
18590     {
18591         e.stopPropagation();
18592         e.preventDefault();
18593         
18594         var target = e.getTarget();
18595         
18596         if(target.nodeName.toLowerCase() === 'i'){
18597             target = Roo.get(target).dom.parentNode;
18598         }
18599         
18600         var nodeName = target.nodeName;
18601         var className = target.className;
18602         var html = target.innerHTML;
18603         //Roo.log(nodeName);
18604         
18605         switch(nodeName.toLowerCase()) {
18606             case 'th':
18607                 switch(className) {
18608                     case 'switch':
18609                         this.showMode(1);
18610                         break;
18611                     case 'prev':
18612                     case 'next':
18613                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18614                         switch(this.viewMode){
18615                                 case 0:
18616                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18617                                         break;
18618                                 case 1:
18619                                 case 2:
18620                                         this.viewDate = this.moveYear(this.viewDate, dir);
18621                                         break;
18622                         }
18623                         this.fill();
18624                         break;
18625                     case 'today':
18626                         var date = new Date();
18627                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18628 //                        this.fill()
18629                         this.setValue(this.formatDate(this.date));
18630                         
18631                         this.hide();
18632                         break;
18633                 }
18634                 break;
18635             case 'span':
18636                 if (className.indexOf('disabled') < 0) {
18637                     this.viewDate.setUTCDate(1);
18638                     if (className.indexOf('month') > -1) {
18639                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18640                     } else {
18641                         var year = parseInt(html, 10) || 0;
18642                         this.viewDate.setUTCFullYear(year);
18643                         
18644                     }
18645                     
18646                     if(this.singleMode){
18647                         this.setValue(this.formatDate(this.viewDate));
18648                         this.hide();
18649                         return;
18650                     }
18651                     
18652                     this.showMode(-1);
18653                     this.fill();
18654                 }
18655                 break;
18656                 
18657             case 'td':
18658                 //Roo.log(className);
18659                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18660                     var day = parseInt(html, 10) || 1;
18661                     var year = this.viewDate.getUTCFullYear(),
18662                         month = this.viewDate.getUTCMonth();
18663
18664                     if (className.indexOf('old') > -1) {
18665                         if(month === 0 ){
18666                             month = 11;
18667                             year -= 1;
18668                         }else{
18669                             month -= 1;
18670                         }
18671                     } else if (className.indexOf('new') > -1) {
18672                         if (month == 11) {
18673                             month = 0;
18674                             year += 1;
18675                         } else {
18676                             month += 1;
18677                         }
18678                     }
18679                     //Roo.log([year,month,day]);
18680                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18681                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18682 //                    this.fill();
18683                     //Roo.log(this.formatDate(this.date));
18684                     this.setValue(this.formatDate(this.date));
18685                     this.hide();
18686                 }
18687                 break;
18688         }
18689     },
18690     
18691     setStartDate: function(startDate)
18692     {
18693         this.startDate = startDate || -Infinity;
18694         if (this.startDate !== -Infinity) {
18695             this.startDate = this.parseDate(this.startDate);
18696         }
18697         this.update();
18698         this.updateNavArrows();
18699     },
18700
18701     setEndDate: function(endDate)
18702     {
18703         this.endDate = endDate || Infinity;
18704         if (this.endDate !== Infinity) {
18705             this.endDate = this.parseDate(this.endDate);
18706         }
18707         this.update();
18708         this.updateNavArrows();
18709     },
18710     
18711     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18712     {
18713         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18714         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18715             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18716         }
18717         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18718             return parseInt(d, 10);
18719         });
18720         this.update();
18721         this.updateNavArrows();
18722     },
18723     
18724     updateNavArrows: function() 
18725     {
18726         if(this.singleMode){
18727             return;
18728         }
18729         
18730         var d = new Date(this.viewDate),
18731         year = d.getUTCFullYear(),
18732         month = d.getUTCMonth();
18733         
18734         Roo.each(this.picker().select('.prev', true).elements, function(v){
18735             v.show();
18736             switch (this.viewMode) {
18737                 case 0:
18738
18739                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18740                         v.hide();
18741                     }
18742                     break;
18743                 case 1:
18744                 case 2:
18745                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18746                         v.hide();
18747                     }
18748                     break;
18749             }
18750         });
18751         
18752         Roo.each(this.picker().select('.next', true).elements, function(v){
18753             v.show();
18754             switch (this.viewMode) {
18755                 case 0:
18756
18757                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18758                         v.hide();
18759                     }
18760                     break;
18761                 case 1:
18762                 case 2:
18763                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18764                         v.hide();
18765                     }
18766                     break;
18767             }
18768         })
18769     },
18770     
18771     moveMonth: function(date, dir)
18772     {
18773         if (!dir) {
18774             return date;
18775         }
18776         var new_date = new Date(date.valueOf()),
18777         day = new_date.getUTCDate(),
18778         month = new_date.getUTCMonth(),
18779         mag = Math.abs(dir),
18780         new_month, test;
18781         dir = dir > 0 ? 1 : -1;
18782         if (mag == 1){
18783             test = dir == -1
18784             // If going back one month, make sure month is not current month
18785             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18786             ? function(){
18787                 return new_date.getUTCMonth() == month;
18788             }
18789             // If going forward one month, make sure month is as expected
18790             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18791             : function(){
18792                 return new_date.getUTCMonth() != new_month;
18793             };
18794             new_month = month + dir;
18795             new_date.setUTCMonth(new_month);
18796             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18797             if (new_month < 0 || new_month > 11) {
18798                 new_month = (new_month + 12) % 12;
18799             }
18800         } else {
18801             // For magnitudes >1, move one month at a time...
18802             for (var i=0; i<mag; i++) {
18803                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18804                 new_date = this.moveMonth(new_date, dir);
18805             }
18806             // ...then reset the day, keeping it in the new month
18807             new_month = new_date.getUTCMonth();
18808             new_date.setUTCDate(day);
18809             test = function(){
18810                 return new_month != new_date.getUTCMonth();
18811             };
18812         }
18813         // Common date-resetting loop -- if date is beyond end of month, make it
18814         // end of month
18815         while (test()){
18816             new_date.setUTCDate(--day);
18817             new_date.setUTCMonth(new_month);
18818         }
18819         return new_date;
18820     },
18821
18822     moveYear: function(date, dir)
18823     {
18824         return this.moveMonth(date, dir*12);
18825     },
18826
18827     dateWithinRange: function(date)
18828     {
18829         return date >= this.startDate && date <= this.endDate;
18830     },
18831
18832     
18833     remove: function() 
18834     {
18835         this.picker().remove();
18836     },
18837     
18838     validateValue : function(value)
18839     {
18840         if(value.length < 1)  {
18841             if(this.allowBlank){
18842                 return true;
18843             }
18844             return false;
18845         }
18846         
18847         if(value.length < this.minLength){
18848             return false;
18849         }
18850         if(value.length > this.maxLength){
18851             return false;
18852         }
18853         if(this.vtype){
18854             var vt = Roo.form.VTypes;
18855             if(!vt[this.vtype](value, this)){
18856                 return false;
18857             }
18858         }
18859         if(typeof this.validator == "function"){
18860             var msg = this.validator(value);
18861             if(msg !== true){
18862                 return false;
18863             }
18864         }
18865         
18866         if(this.regex && !this.regex.test(value)){
18867             return false;
18868         }
18869         
18870         if(typeof(this.parseDate(value)) == 'undefined'){
18871             return false;
18872         }
18873         
18874         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18875             return false;
18876         }      
18877         
18878         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18879             return false;
18880         } 
18881         
18882         
18883         return true;
18884     }
18885    
18886 });
18887
18888 Roo.apply(Roo.bootstrap.DateField,  {
18889     
18890     head : {
18891         tag: 'thead',
18892         cn: [
18893         {
18894             tag: 'tr',
18895             cn: [
18896             {
18897                 tag: 'th',
18898                 cls: 'prev',
18899                 html: '<i class="fa fa-arrow-left"/>'
18900             },
18901             {
18902                 tag: 'th',
18903                 cls: 'switch',
18904                 colspan: '5'
18905             },
18906             {
18907                 tag: 'th',
18908                 cls: 'next',
18909                 html: '<i class="fa fa-arrow-right"/>'
18910             }
18911
18912             ]
18913         }
18914         ]
18915     },
18916     
18917     content : {
18918         tag: 'tbody',
18919         cn: [
18920         {
18921             tag: 'tr',
18922             cn: [
18923             {
18924                 tag: 'td',
18925                 colspan: '7'
18926             }
18927             ]
18928         }
18929         ]
18930     },
18931     
18932     footer : {
18933         tag: 'tfoot',
18934         cn: [
18935         {
18936             tag: 'tr',
18937             cn: [
18938             {
18939                 tag: 'th',
18940                 colspan: '7',
18941                 cls: 'today'
18942             }
18943                     
18944             ]
18945         }
18946         ]
18947     },
18948     
18949     dates:{
18950         en: {
18951             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18952             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18953             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18954             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18955             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18956             today: "Today"
18957         }
18958     },
18959     
18960     modes: [
18961     {
18962         clsName: 'days',
18963         navFnc: 'Month',
18964         navStep: 1
18965     },
18966     {
18967         clsName: 'months',
18968         navFnc: 'FullYear',
18969         navStep: 1
18970     },
18971     {
18972         clsName: 'years',
18973         navFnc: 'FullYear',
18974         navStep: 10
18975     }]
18976 });
18977
18978 Roo.apply(Roo.bootstrap.DateField,  {
18979   
18980     template : {
18981         tag: 'div',
18982         cls: 'datepicker dropdown-menu roo-dynamic',
18983         cn: [
18984         {
18985             tag: 'div',
18986             cls: 'datepicker-days',
18987             cn: [
18988             {
18989                 tag: 'table',
18990                 cls: 'table-condensed',
18991                 cn:[
18992                 Roo.bootstrap.DateField.head,
18993                 {
18994                     tag: 'tbody'
18995                 },
18996                 Roo.bootstrap.DateField.footer
18997                 ]
18998             }
18999             ]
19000         },
19001         {
19002             tag: 'div',
19003             cls: 'datepicker-months',
19004             cn: [
19005             {
19006                 tag: 'table',
19007                 cls: 'table-condensed',
19008                 cn:[
19009                 Roo.bootstrap.DateField.head,
19010                 Roo.bootstrap.DateField.content,
19011                 Roo.bootstrap.DateField.footer
19012                 ]
19013             }
19014             ]
19015         },
19016         {
19017             tag: 'div',
19018             cls: 'datepicker-years',
19019             cn: [
19020             {
19021                 tag: 'table',
19022                 cls: 'table-condensed',
19023                 cn:[
19024                 Roo.bootstrap.DateField.head,
19025                 Roo.bootstrap.DateField.content,
19026                 Roo.bootstrap.DateField.footer
19027                 ]
19028             }
19029             ]
19030         }
19031         ]
19032     }
19033 });
19034
19035  
19036
19037  /*
19038  * - LGPL
19039  *
19040  * TimeField
19041  * 
19042  */
19043
19044 /**
19045  * @class Roo.bootstrap.TimeField
19046  * @extends Roo.bootstrap.Input
19047  * Bootstrap DateField class
19048  * 
19049  * 
19050  * @constructor
19051  * Create a new TimeField
19052  * @param {Object} config The config object
19053  */
19054
19055 Roo.bootstrap.TimeField = function(config){
19056     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19057     this.addEvents({
19058             /**
19059              * @event show
19060              * Fires when this field show.
19061              * @param {Roo.bootstrap.DateField} thisthis
19062              * @param {Mixed} date The date value
19063              */
19064             show : true,
19065             /**
19066              * @event show
19067              * Fires when this field hide.
19068              * @param {Roo.bootstrap.DateField} this
19069              * @param {Mixed} date The date value
19070              */
19071             hide : true,
19072             /**
19073              * @event select
19074              * Fires when select a date.
19075              * @param {Roo.bootstrap.DateField} this
19076              * @param {Mixed} date The date value
19077              */
19078             select : true
19079         });
19080 };
19081
19082 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19083     
19084     /**
19085      * @cfg {String} format
19086      * The default time format string which can be overriden for localization support.  The format must be
19087      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19088      */
19089     format : "H:i",
19090        
19091     onRender: function(ct, position)
19092     {
19093         
19094         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19095                 
19096         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19097         
19098         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19099         
19100         this.pop = this.picker().select('>.datepicker-time',true).first();
19101         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19102         
19103         this.picker().on('mousedown', this.onMousedown, this);
19104         this.picker().on('click', this.onClick, this);
19105         
19106         this.picker().addClass('datepicker-dropdown');
19107     
19108         this.fillTime();
19109         this.update();
19110             
19111         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19112         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19113         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19114         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19115         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19116         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19117
19118     },
19119     
19120     fireKey: function(e){
19121         if (!this.picker().isVisible()){
19122             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19123                 this.show();
19124             }
19125             return;
19126         }
19127
19128         e.preventDefault();
19129         
19130         switch(e.keyCode){
19131             case 27: // escape
19132                 this.hide();
19133                 break;
19134             case 37: // left
19135             case 39: // right
19136                 this.onTogglePeriod();
19137                 break;
19138             case 38: // up
19139                 this.onIncrementMinutes();
19140                 break;
19141             case 40: // down
19142                 this.onDecrementMinutes();
19143                 break;
19144             case 13: // enter
19145             case 9: // tab
19146                 this.setTime();
19147                 break;
19148         }
19149     },
19150     
19151     onClick: function(e) {
19152         e.stopPropagation();
19153         e.preventDefault();
19154     },
19155     
19156     picker : function()
19157     {
19158         return this.el.select('.datepicker', true).first();
19159     },
19160     
19161     fillTime: function()
19162     {    
19163         var time = this.pop.select('tbody', true).first();
19164         
19165         time.dom.innerHTML = '';
19166         
19167         time.createChild({
19168             tag: 'tr',
19169             cn: [
19170                 {
19171                     tag: 'td',
19172                     cn: [
19173                         {
19174                             tag: 'a',
19175                             href: '#',
19176                             cls: 'btn',
19177                             cn: [
19178                                 {
19179                                     tag: 'span',
19180                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19181                                 }
19182                             ]
19183                         } 
19184                     ]
19185                 },
19186                 {
19187                     tag: 'td',
19188                     cls: 'separator'
19189                 },
19190                 {
19191                     tag: 'td',
19192                     cn: [
19193                         {
19194                             tag: 'a',
19195                             href: '#',
19196                             cls: 'btn',
19197                             cn: [
19198                                 {
19199                                     tag: 'span',
19200                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19201                                 }
19202                             ]
19203                         }
19204                     ]
19205                 },
19206                 {
19207                     tag: 'td',
19208                     cls: 'separator'
19209                 }
19210             ]
19211         });
19212         
19213         time.createChild({
19214             tag: 'tr',
19215             cn: [
19216                 {
19217                     tag: 'td',
19218                     cn: [
19219                         {
19220                             tag: 'span',
19221                             cls: 'timepicker-hour',
19222                             html: '00'
19223                         }  
19224                     ]
19225                 },
19226                 {
19227                     tag: 'td',
19228                     cls: 'separator',
19229                     html: ':'
19230                 },
19231                 {
19232                     tag: 'td',
19233                     cn: [
19234                         {
19235                             tag: 'span',
19236                             cls: 'timepicker-minute',
19237                             html: '00'
19238                         }  
19239                     ]
19240                 },
19241                 {
19242                     tag: 'td',
19243                     cls: 'separator'
19244                 },
19245                 {
19246                     tag: 'td',
19247                     cn: [
19248                         {
19249                             tag: 'button',
19250                             type: 'button',
19251                             cls: 'btn btn-primary period',
19252                             html: 'AM'
19253                             
19254                         }
19255                     ]
19256                 }
19257             ]
19258         });
19259         
19260         time.createChild({
19261             tag: 'tr',
19262             cn: [
19263                 {
19264                     tag: 'td',
19265                     cn: [
19266                         {
19267                             tag: 'a',
19268                             href: '#',
19269                             cls: 'btn',
19270                             cn: [
19271                                 {
19272                                     tag: 'span',
19273                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19274                                 }
19275                             ]
19276                         }
19277                     ]
19278                 },
19279                 {
19280                     tag: 'td',
19281                     cls: 'separator'
19282                 },
19283                 {
19284                     tag: 'td',
19285                     cn: [
19286                         {
19287                             tag: 'a',
19288                             href: '#',
19289                             cls: 'btn',
19290                             cn: [
19291                                 {
19292                                     tag: 'span',
19293                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19294                                 }
19295                             ]
19296                         }
19297                     ]
19298                 },
19299                 {
19300                     tag: 'td',
19301                     cls: 'separator'
19302                 }
19303             ]
19304         });
19305         
19306     },
19307     
19308     update: function()
19309     {
19310         
19311         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19312         
19313         this.fill();
19314     },
19315     
19316     fill: function() 
19317     {
19318         var hours = this.time.getHours();
19319         var minutes = this.time.getMinutes();
19320         var period = 'AM';
19321         
19322         if(hours > 11){
19323             period = 'PM';
19324         }
19325         
19326         if(hours == 0){
19327             hours = 12;
19328         }
19329         
19330         
19331         if(hours > 12){
19332             hours = hours - 12;
19333         }
19334         
19335         if(hours < 10){
19336             hours = '0' + hours;
19337         }
19338         
19339         if(minutes < 10){
19340             minutes = '0' + minutes;
19341         }
19342         
19343         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19344         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19345         this.pop.select('button', true).first().dom.innerHTML = period;
19346         
19347     },
19348     
19349     place: function()
19350     {   
19351         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19352         
19353         var cls = ['bottom'];
19354         
19355         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19356             cls.pop();
19357             cls.push('top');
19358         }
19359         
19360         cls.push('right');
19361         
19362         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19363             cls.pop();
19364             cls.push('left');
19365         }
19366         
19367         this.picker().addClass(cls.join('-'));
19368         
19369         var _this = this;
19370         
19371         Roo.each(cls, function(c){
19372             if(c == 'bottom'){
19373                 _this.picker().setTop(_this.inputEl().getHeight());
19374                 return;
19375             }
19376             if(c == 'top'){
19377                 _this.picker().setTop(0 - _this.picker().getHeight());
19378                 return;
19379             }
19380             
19381             if(c == 'left'){
19382                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19383                 return;
19384             }
19385             if(c == 'right'){
19386                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19387                 return;
19388             }
19389         });
19390         
19391     },
19392   
19393     onFocus : function()
19394     {
19395         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19396         this.show();
19397     },
19398     
19399     onBlur : function()
19400     {
19401         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19402         this.hide();
19403     },
19404     
19405     show : function()
19406     {
19407         this.picker().show();
19408         this.pop.show();
19409         this.update();
19410         this.place();
19411         
19412         this.fireEvent('show', this, this.date);
19413     },
19414     
19415     hide : function()
19416     {
19417         this.picker().hide();
19418         this.pop.hide();
19419         
19420         this.fireEvent('hide', this, this.date);
19421     },
19422     
19423     setTime : function()
19424     {
19425         this.hide();
19426         this.setValue(this.time.format(this.format));
19427         
19428         this.fireEvent('select', this, this.date);
19429         
19430         
19431     },
19432     
19433     onMousedown: function(e){
19434         e.stopPropagation();
19435         e.preventDefault();
19436     },
19437     
19438     onIncrementHours: function()
19439     {
19440         Roo.log('onIncrementHours');
19441         this.time = this.time.add(Date.HOUR, 1);
19442         this.update();
19443         
19444     },
19445     
19446     onDecrementHours: function()
19447     {
19448         Roo.log('onDecrementHours');
19449         this.time = this.time.add(Date.HOUR, -1);
19450         this.update();
19451     },
19452     
19453     onIncrementMinutes: function()
19454     {
19455         Roo.log('onIncrementMinutes');
19456         this.time = this.time.add(Date.MINUTE, 1);
19457         this.update();
19458     },
19459     
19460     onDecrementMinutes: function()
19461     {
19462         Roo.log('onDecrementMinutes');
19463         this.time = this.time.add(Date.MINUTE, -1);
19464         this.update();
19465     },
19466     
19467     onTogglePeriod: function()
19468     {
19469         Roo.log('onTogglePeriod');
19470         this.time = this.time.add(Date.HOUR, 12);
19471         this.update();
19472     }
19473     
19474    
19475 });
19476
19477 Roo.apply(Roo.bootstrap.TimeField,  {
19478     
19479     content : {
19480         tag: 'tbody',
19481         cn: [
19482             {
19483                 tag: 'tr',
19484                 cn: [
19485                 {
19486                     tag: 'td',
19487                     colspan: '7'
19488                 }
19489                 ]
19490             }
19491         ]
19492     },
19493     
19494     footer : {
19495         tag: 'tfoot',
19496         cn: [
19497             {
19498                 tag: 'tr',
19499                 cn: [
19500                 {
19501                     tag: 'th',
19502                     colspan: '7',
19503                     cls: '',
19504                     cn: [
19505                         {
19506                             tag: 'button',
19507                             cls: 'btn btn-info ok',
19508                             html: 'OK'
19509                         }
19510                     ]
19511                 }
19512
19513                 ]
19514             }
19515         ]
19516     }
19517 });
19518
19519 Roo.apply(Roo.bootstrap.TimeField,  {
19520   
19521     template : {
19522         tag: 'div',
19523         cls: 'datepicker dropdown-menu',
19524         cn: [
19525             {
19526                 tag: 'div',
19527                 cls: 'datepicker-time',
19528                 cn: [
19529                 {
19530                     tag: 'table',
19531                     cls: 'table-condensed',
19532                     cn:[
19533                     Roo.bootstrap.TimeField.content,
19534                     Roo.bootstrap.TimeField.footer
19535                     ]
19536                 }
19537                 ]
19538             }
19539         ]
19540     }
19541 });
19542
19543  
19544
19545  /*
19546  * - LGPL
19547  *
19548  * MonthField
19549  * 
19550  */
19551
19552 /**
19553  * @class Roo.bootstrap.MonthField
19554  * @extends Roo.bootstrap.Input
19555  * Bootstrap MonthField class
19556  * 
19557  * @cfg {String} language default en
19558  * 
19559  * @constructor
19560  * Create a new MonthField
19561  * @param {Object} config The config object
19562  */
19563
19564 Roo.bootstrap.MonthField = function(config){
19565     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19566     
19567     this.addEvents({
19568         /**
19569          * @event show
19570          * Fires when this field show.
19571          * @param {Roo.bootstrap.MonthField} this
19572          * @param {Mixed} date The date value
19573          */
19574         show : true,
19575         /**
19576          * @event show
19577          * Fires when this field hide.
19578          * @param {Roo.bootstrap.MonthField} this
19579          * @param {Mixed} date The date value
19580          */
19581         hide : true,
19582         /**
19583          * @event select
19584          * Fires when select a date.
19585          * @param {Roo.bootstrap.MonthField} this
19586          * @param {String} oldvalue The old value
19587          * @param {String} newvalue The new value
19588          */
19589         select : true
19590     });
19591 };
19592
19593 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19594     
19595     onRender: function(ct, position)
19596     {
19597         
19598         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19599         
19600         this.language = this.language || 'en';
19601         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19602         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19603         
19604         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19605         this.isInline = false;
19606         this.isInput = true;
19607         this.component = this.el.select('.add-on', true).first() || false;
19608         this.component = (this.component && this.component.length === 0) ? false : this.component;
19609         this.hasInput = this.component && this.inputEL().length;
19610         
19611         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19612         
19613         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19614         
19615         this.picker().on('mousedown', this.onMousedown, this);
19616         this.picker().on('click', this.onClick, this);
19617         
19618         this.picker().addClass('datepicker-dropdown');
19619         
19620         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19621             v.setStyle('width', '189px');
19622         });
19623         
19624         this.fillMonths();
19625         
19626         this.update();
19627         
19628         if(this.isInline) {
19629             this.show();
19630         }
19631         
19632     },
19633     
19634     setValue: function(v, suppressEvent)
19635     {   
19636         var o = this.getValue();
19637         
19638         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19639         
19640         this.update();
19641
19642         if(suppressEvent !== true){
19643             this.fireEvent('select', this, o, v);
19644         }
19645         
19646     },
19647     
19648     getValue: function()
19649     {
19650         return this.value;
19651     },
19652     
19653     onClick: function(e) 
19654     {
19655         e.stopPropagation();
19656         e.preventDefault();
19657         
19658         var target = e.getTarget();
19659         
19660         if(target.nodeName.toLowerCase() === 'i'){
19661             target = Roo.get(target).dom.parentNode;
19662         }
19663         
19664         var nodeName = target.nodeName;
19665         var className = target.className;
19666         var html = target.innerHTML;
19667         
19668         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19669             return;
19670         }
19671         
19672         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19673         
19674         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19675         
19676         this.hide();
19677                         
19678     },
19679     
19680     picker : function()
19681     {
19682         return this.pickerEl;
19683     },
19684     
19685     fillMonths: function()
19686     {    
19687         var i = 0;
19688         var months = this.picker().select('>.datepicker-months td', true).first();
19689         
19690         months.dom.innerHTML = '';
19691         
19692         while (i < 12) {
19693             var month = {
19694                 tag: 'span',
19695                 cls: 'month',
19696                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19697             };
19698             
19699             months.createChild(month);
19700         }
19701         
19702     },
19703     
19704     update: function()
19705     {
19706         var _this = this;
19707         
19708         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19709             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19710         }
19711         
19712         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19713             e.removeClass('active');
19714             
19715             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19716                 e.addClass('active');
19717             }
19718         })
19719     },
19720     
19721     place: function()
19722     {
19723         if(this.isInline) {
19724             return;
19725         }
19726         
19727         this.picker().removeClass(['bottom', 'top']);
19728         
19729         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19730             /*
19731              * place to the top of element!
19732              *
19733              */
19734             
19735             this.picker().addClass('top');
19736             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19737             
19738             return;
19739         }
19740         
19741         this.picker().addClass('bottom');
19742         
19743         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19744     },
19745     
19746     onFocus : function()
19747     {
19748         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19749         this.show();
19750     },
19751     
19752     onBlur : function()
19753     {
19754         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19755         
19756         var d = this.inputEl().getValue();
19757         
19758         this.setValue(d);
19759                 
19760         this.hide();
19761     },
19762     
19763     show : function()
19764     {
19765         this.picker().show();
19766         this.picker().select('>.datepicker-months', true).first().show();
19767         this.update();
19768         this.place();
19769         
19770         this.fireEvent('show', this, this.date);
19771     },
19772     
19773     hide : function()
19774     {
19775         if(this.isInline) {
19776             return;
19777         }
19778         this.picker().hide();
19779         this.fireEvent('hide', this, this.date);
19780         
19781     },
19782     
19783     onMousedown: function(e)
19784     {
19785         e.stopPropagation();
19786         e.preventDefault();
19787     },
19788     
19789     keyup: function(e)
19790     {
19791         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19792         this.update();
19793     },
19794
19795     fireKey: function(e)
19796     {
19797         if (!this.picker().isVisible()){
19798             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19799                 this.show();
19800             }
19801             return;
19802         }
19803         
19804         var dir;
19805         
19806         switch(e.keyCode){
19807             case 27: // escape
19808                 this.hide();
19809                 e.preventDefault();
19810                 break;
19811             case 37: // left
19812             case 39: // right
19813                 dir = e.keyCode == 37 ? -1 : 1;
19814                 
19815                 this.vIndex = this.vIndex + dir;
19816                 
19817                 if(this.vIndex < 0){
19818                     this.vIndex = 0;
19819                 }
19820                 
19821                 if(this.vIndex > 11){
19822                     this.vIndex = 11;
19823                 }
19824                 
19825                 if(isNaN(this.vIndex)){
19826                     this.vIndex = 0;
19827                 }
19828                 
19829                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19830                 
19831                 break;
19832             case 38: // up
19833             case 40: // down
19834                 
19835                 dir = e.keyCode == 38 ? -1 : 1;
19836                 
19837                 this.vIndex = this.vIndex + dir * 4;
19838                 
19839                 if(this.vIndex < 0){
19840                     this.vIndex = 0;
19841                 }
19842                 
19843                 if(this.vIndex > 11){
19844                     this.vIndex = 11;
19845                 }
19846                 
19847                 if(isNaN(this.vIndex)){
19848                     this.vIndex = 0;
19849                 }
19850                 
19851                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19852                 break;
19853                 
19854             case 13: // enter
19855                 
19856                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19857                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19858                 }
19859                 
19860                 this.hide();
19861                 e.preventDefault();
19862                 break;
19863             case 9: // tab
19864                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19865                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19866                 }
19867                 this.hide();
19868                 break;
19869             case 16: // shift
19870             case 17: // ctrl
19871             case 18: // alt
19872                 break;
19873             default :
19874                 this.hide();
19875                 
19876         }
19877     },
19878     
19879     remove: function() 
19880     {
19881         this.picker().remove();
19882     }
19883    
19884 });
19885
19886 Roo.apply(Roo.bootstrap.MonthField,  {
19887     
19888     content : {
19889         tag: 'tbody',
19890         cn: [
19891         {
19892             tag: 'tr',
19893             cn: [
19894             {
19895                 tag: 'td',
19896                 colspan: '7'
19897             }
19898             ]
19899         }
19900         ]
19901     },
19902     
19903     dates:{
19904         en: {
19905             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19906             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19907         }
19908     }
19909 });
19910
19911 Roo.apply(Roo.bootstrap.MonthField,  {
19912   
19913     template : {
19914         tag: 'div',
19915         cls: 'datepicker dropdown-menu roo-dynamic',
19916         cn: [
19917             {
19918                 tag: 'div',
19919                 cls: 'datepicker-months',
19920                 cn: [
19921                 {
19922                     tag: 'table',
19923                     cls: 'table-condensed',
19924                     cn:[
19925                         Roo.bootstrap.DateField.content
19926                     ]
19927                 }
19928                 ]
19929             }
19930         ]
19931     }
19932 });
19933
19934  
19935
19936  
19937  /*
19938  * - LGPL
19939  *
19940  * CheckBox
19941  * 
19942  */
19943
19944 /**
19945  * @class Roo.bootstrap.CheckBox
19946  * @extends Roo.bootstrap.Input
19947  * Bootstrap CheckBox class
19948  * 
19949  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19950  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19951  * @cfg {String} boxLabel The text that appears beside the checkbox
19952  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19953  * @cfg {Boolean} checked initnal the element
19954  * @cfg {Boolean} inline inline the element (default false)
19955  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19956  * 
19957  * @constructor
19958  * Create a new CheckBox
19959  * @param {Object} config The config object
19960  */
19961
19962 Roo.bootstrap.CheckBox = function(config){
19963     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19964    
19965     this.addEvents({
19966         /**
19967         * @event check
19968         * Fires when the element is checked or unchecked.
19969         * @param {Roo.bootstrap.CheckBox} this This input
19970         * @param {Boolean} checked The new checked value
19971         */
19972        check : true
19973     });
19974     
19975 };
19976
19977 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
19978   
19979     inputType: 'checkbox',
19980     inputValue: 1,
19981     valueOff: 0,
19982     boxLabel: false,
19983     checked: false,
19984     weight : false,
19985     inline: false,
19986     
19987     getAutoCreate : function()
19988     {
19989         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19990         
19991         var id = Roo.id();
19992         
19993         var cfg = {};
19994         
19995         cfg.cls = 'form-group ' + this.inputType; //input-group
19996         
19997         if(this.inline){
19998             cfg.cls += ' ' + this.inputType + '-inline';
19999         }
20000         
20001         var input =  {
20002             tag: 'input',
20003             id : id,
20004             type : this.inputType,
20005             value : this.inputValue,
20006             cls : 'roo-' + this.inputType, //'form-box',
20007             placeholder : this.placeholder || ''
20008             
20009         };
20010         
20011         if(this.inputType != 'radio'){
20012             var hidden =  {
20013                 tag: 'input',
20014                 type : 'hidden',
20015                 cls : 'roo-hidden-value',
20016                 value : this.checked ? this.valueOff : this.inputValue
20017             };
20018         }
20019         
20020             
20021         if (this.weight) { // Validity check?
20022             cfg.cls += " " + this.inputType + "-" + this.weight;
20023         }
20024         
20025         if (this.disabled) {
20026             input.disabled=true;
20027         }
20028         
20029         if(this.checked){
20030             input.checked = this.checked;
20031             
20032         }
20033         
20034         
20035         if (this.name) {
20036             
20037             input.name = this.name;
20038             
20039             if(this.inputType != 'radio'){
20040                 hidden.name = this.name;
20041                 input.name = '_hidden_' + this.name;
20042             }
20043         }
20044         
20045         if (this.size) {
20046             input.cls += ' input-' + this.size;
20047         }
20048         
20049         var settings=this;
20050         
20051         ['xs','sm','md','lg'].map(function(size){
20052             if (settings[size]) {
20053                 cfg.cls += ' col-' + size + '-' + settings[size];
20054             }
20055         });
20056         
20057         var inputblock = input;
20058          
20059         if (this.before || this.after) {
20060             
20061             inputblock = {
20062                 cls : 'input-group',
20063                 cn :  [] 
20064             };
20065             
20066             if (this.before) {
20067                 inputblock.cn.push({
20068                     tag :'span',
20069                     cls : 'input-group-addon',
20070                     html : this.before
20071                 });
20072             }
20073             
20074             inputblock.cn.push(input);
20075             
20076             if(this.inputType != 'radio'){
20077                 inputblock.cn.push(hidden);
20078             }
20079             
20080             if (this.after) {
20081                 inputblock.cn.push({
20082                     tag :'span',
20083                     cls : 'input-group-addon',
20084                     html : this.after
20085                 });
20086             }
20087             
20088         }
20089         
20090         if (align ==='left' && this.fieldLabel.length) {
20091 //                Roo.log("left and has label");
20092             cfg.cn = [
20093                 {
20094                     tag: 'label',
20095                     'for' :  id,
20096                     cls : 'control-label',
20097                     html : this.fieldLabel
20098
20099                 },
20100                 {
20101                     cls : "", 
20102                     cn: [
20103                         inputblock
20104                     ]
20105                 }
20106             ];
20107             
20108             if(this.labelWidth > 12){
20109                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20110             }
20111             
20112             if(this.labelWidth < 13 && this.labelmd == 0){
20113                 this.labelmd = this.labelWidth;
20114             }
20115             
20116             if(this.labellg > 0){
20117                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20118                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20119             }
20120             
20121             if(this.labelmd > 0){
20122                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20123                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20124             }
20125             
20126             if(this.labelsm > 0){
20127                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20128                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20129             }
20130             
20131             if(this.labelxs > 0){
20132                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20133                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20134             }
20135             
20136         } else if ( this.fieldLabel.length) {
20137 //                Roo.log(" label");
20138                 cfg.cn = [
20139                    
20140                     {
20141                         tag: this.boxLabel ? 'span' : 'label',
20142                         'for': id,
20143                         cls: 'control-label box-input-label',
20144                         //cls : 'input-group-addon',
20145                         html : this.fieldLabel
20146                         
20147                     },
20148                     
20149                     inputblock
20150                     
20151                 ];
20152
20153         } else {
20154             
20155 //                Roo.log(" no label && no align");
20156                 cfg.cn = [  inputblock ] ;
20157                 
20158                 
20159         }
20160         
20161         if(this.boxLabel){
20162              var boxLabelCfg = {
20163                 tag: 'label',
20164                 //'for': id, // box label is handled by onclick - so no for...
20165                 cls: 'box-label',
20166                 html: this.boxLabel
20167             };
20168             
20169             if(this.tooltip){
20170                 boxLabelCfg.tooltip = this.tooltip;
20171             }
20172              
20173             cfg.cn.push(boxLabelCfg);
20174         }
20175         
20176         if(this.inputType != 'radio'){
20177             cfg.cn.push(hidden);
20178         }
20179         
20180         return cfg;
20181         
20182     },
20183     
20184     /**
20185      * return the real input element.
20186      */
20187     inputEl: function ()
20188     {
20189         return this.el.select('input.roo-' + this.inputType,true).first();
20190     },
20191     hiddenEl: function ()
20192     {
20193         return this.el.select('input.roo-hidden-value',true).first();
20194     },
20195     
20196     labelEl: function()
20197     {
20198         return this.el.select('label.control-label',true).first();
20199     },
20200     /* depricated... */
20201     
20202     label: function()
20203     {
20204         return this.labelEl();
20205     },
20206     
20207     boxLabelEl: function()
20208     {
20209         return this.el.select('label.box-label',true).first();
20210     },
20211     
20212     initEvents : function()
20213     {
20214 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20215         
20216         this.inputEl().on('click', this.onClick,  this);
20217         
20218         if (this.boxLabel) { 
20219             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20220         }
20221         
20222         this.startValue = this.getValue();
20223         
20224         if(this.groupId){
20225             Roo.bootstrap.CheckBox.register(this);
20226         }
20227     },
20228     
20229     onClick : function()
20230     {   
20231         this.setChecked(!this.checked);
20232     },
20233     
20234     setChecked : function(state,suppressEvent)
20235     {
20236         this.startValue = this.getValue();
20237
20238         if(this.inputType == 'radio'){
20239             
20240             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20241                 e.dom.checked = false;
20242             });
20243             
20244             this.inputEl().dom.checked = true;
20245             
20246             this.inputEl().dom.value = this.inputValue;
20247             
20248             if(suppressEvent !== true){
20249                 this.fireEvent('check', this, true);
20250             }
20251             
20252             this.validate();
20253             
20254             return;
20255         }
20256         
20257         this.checked = state;
20258         
20259         this.inputEl().dom.checked = state;
20260         
20261         
20262         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20263         
20264         if(suppressEvent !== true){
20265             this.fireEvent('check', this, state);
20266         }
20267         
20268         this.validate();
20269     },
20270     
20271     getValue : function()
20272     {
20273         if(this.inputType == 'radio'){
20274             return this.getGroupValue();
20275         }
20276         
20277         return this.hiddenEl().dom.value;
20278         
20279     },
20280     
20281     getGroupValue : function()
20282     {
20283         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20284             return '';
20285         }
20286         
20287         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20288     },
20289     
20290     setValue : function(v,suppressEvent)
20291     {
20292         if(this.inputType == 'radio'){
20293             this.setGroupValue(v, suppressEvent);
20294             return;
20295         }
20296         
20297         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20298         
20299         this.validate();
20300     },
20301     
20302     setGroupValue : function(v, suppressEvent)
20303     {
20304         this.startValue = this.getValue();
20305         
20306         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20307             e.dom.checked = false;
20308             
20309             if(e.dom.value == v){
20310                 e.dom.checked = true;
20311             }
20312         });
20313         
20314         if(suppressEvent !== true){
20315             this.fireEvent('check', this, true);
20316         }
20317
20318         this.validate();
20319         
20320         return;
20321     },
20322     
20323     validate : function()
20324     {
20325         if(
20326                 this.disabled || 
20327                 (this.inputType == 'radio' && this.validateRadio()) ||
20328                 (this.inputType == 'checkbox' && this.validateCheckbox())
20329         ){
20330             this.markValid();
20331             return true;
20332         }
20333         
20334         this.markInvalid();
20335         return false;
20336     },
20337     
20338     validateRadio : function()
20339     {
20340         if(this.allowBlank){
20341             return true;
20342         }
20343         
20344         var valid = false;
20345         
20346         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20347             if(!e.dom.checked){
20348                 return;
20349             }
20350             
20351             valid = true;
20352             
20353             return false;
20354         });
20355         
20356         return valid;
20357     },
20358     
20359     validateCheckbox : function()
20360     {
20361         if(!this.groupId){
20362             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20363             //return (this.getValue() == this.inputValue) ? true : false;
20364         }
20365         
20366         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20367         
20368         if(!group){
20369             return false;
20370         }
20371         
20372         var r = false;
20373         
20374         for(var i in group){
20375             if(r){
20376                 break;
20377             }
20378             
20379             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20380         }
20381         
20382         return r;
20383     },
20384     
20385     /**
20386      * Mark this field as valid
20387      */
20388     markValid : function()
20389     {
20390         var _this = this;
20391         
20392         this.fireEvent('valid', this);
20393         
20394         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20395         
20396         if(this.groupId){
20397             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20398         }
20399         
20400         if(label){
20401             label.markValid();
20402         }
20403
20404         if(this.inputType == 'radio'){
20405             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20406                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20407                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20408             });
20409             
20410             return;
20411         }
20412
20413         if(!this.groupId){
20414             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20415             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20416             return;
20417         }
20418         
20419         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20420         
20421         if(!group){
20422             return;
20423         }
20424         
20425         for(var i in group){
20426             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20427             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20428         }
20429     },
20430     
20431      /**
20432      * Mark this field as invalid
20433      * @param {String} msg The validation message
20434      */
20435     markInvalid : function(msg)
20436     {
20437         if(this.allowBlank){
20438             return;
20439         }
20440         
20441         var _this = this;
20442         
20443         this.fireEvent('invalid', this, msg);
20444         
20445         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20446         
20447         if(this.groupId){
20448             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20449         }
20450         
20451         if(label){
20452             label.markInvalid();
20453         }
20454             
20455         if(this.inputType == 'radio'){
20456             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20457                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20458                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20459             });
20460             
20461             return;
20462         }
20463         
20464         if(!this.groupId){
20465             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20466             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20467             return;
20468         }
20469         
20470         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20471         
20472         if(!group){
20473             return;
20474         }
20475         
20476         for(var i in group){
20477             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20478             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20479         }
20480         
20481     },
20482     
20483     clearInvalid : function()
20484     {
20485         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20486         
20487         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20488         
20489         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20490         
20491         if (label) {
20492             label.iconEl.removeClass(label.validClass);
20493             label.iconEl.removeClass(label.invalidClass);
20494         }
20495     },
20496     
20497     disable : function()
20498     {
20499         if(this.inputType != 'radio'){
20500             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20501             return;
20502         }
20503         
20504         var _this = this;
20505         
20506         if(this.rendered){
20507             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20508                 _this.getActionEl().addClass(this.disabledClass);
20509                 e.dom.disabled = true;
20510             });
20511         }
20512         
20513         this.disabled = true;
20514         this.fireEvent("disable", this);
20515         return this;
20516     },
20517
20518     enable : function()
20519     {
20520         if(this.inputType != 'radio'){
20521             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20522             return;
20523         }
20524         
20525         var _this = this;
20526         
20527         if(this.rendered){
20528             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20529                 _this.getActionEl().removeClass(this.disabledClass);
20530                 e.dom.disabled = false;
20531             });
20532         }
20533         
20534         this.disabled = false;
20535         this.fireEvent("enable", this);
20536         return this;
20537     }
20538
20539 });
20540
20541 Roo.apply(Roo.bootstrap.CheckBox, {
20542     
20543     groups: {},
20544     
20545      /**
20546     * register a CheckBox Group
20547     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20548     */
20549     register : function(checkbox)
20550     {
20551         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20552             this.groups[checkbox.groupId] = {};
20553         }
20554         
20555         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20556             return;
20557         }
20558         
20559         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20560         
20561     },
20562     /**
20563     * fetch a CheckBox Group based on the group ID
20564     * @param {string} the group ID
20565     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20566     */
20567     get: function(groupId) {
20568         if (typeof(this.groups[groupId]) == 'undefined') {
20569             return false;
20570         }
20571         
20572         return this.groups[groupId] ;
20573     }
20574     
20575     
20576 });
20577 /*
20578  * - LGPL
20579  *
20580  * RadioItem
20581  * 
20582  */
20583
20584 /**
20585  * @class Roo.bootstrap.Radio
20586  * @extends Roo.bootstrap.Component
20587  * Bootstrap Radio class
20588  * @cfg {String} boxLabel - the label associated
20589  * @cfg {String} value - the value of radio
20590  * 
20591  * @constructor
20592  * Create a new Radio
20593  * @param {Object} config The config object
20594  */
20595 Roo.bootstrap.Radio = function(config){
20596     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20597     
20598 };
20599
20600 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20601     
20602     boxLabel : '',
20603     
20604     value : '',
20605     
20606     getAutoCreate : function()
20607     {
20608         var cfg = {
20609             tag : 'div',
20610             cls : 'form-group radio',
20611             cn : [
20612                 {
20613                     tag : 'label',
20614                     cls : 'box-label',
20615                     html : this.boxLabel
20616                 }
20617             ]
20618         };
20619         
20620         return cfg;
20621     },
20622     
20623     initEvents : function() 
20624     {
20625         this.parent().register(this);
20626         
20627         this.el.on('click', this.onClick, this);
20628         
20629     },
20630     
20631     onClick : function()
20632     {
20633         this.setChecked(true);
20634     },
20635     
20636     setChecked : function(state, suppressEvent)
20637     {
20638         this.parent().setValue(this.value, suppressEvent);
20639         
20640     }
20641     
20642 });
20643  
20644
20645  //<script type="text/javascript">
20646
20647 /*
20648  * Based  Ext JS Library 1.1.1
20649  * Copyright(c) 2006-2007, Ext JS, LLC.
20650  * LGPL
20651  *
20652  */
20653  
20654 /**
20655  * @class Roo.HtmlEditorCore
20656  * @extends Roo.Component
20657  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20658  *
20659  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20660  */
20661
20662 Roo.HtmlEditorCore = function(config){
20663     
20664     
20665     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20666     
20667     
20668     this.addEvents({
20669         /**
20670          * @event initialize
20671          * Fires when the editor is fully initialized (including the iframe)
20672          * @param {Roo.HtmlEditorCore} this
20673          */
20674         initialize: true,
20675         /**
20676          * @event activate
20677          * Fires when the editor is first receives the focus. Any insertion must wait
20678          * until after this event.
20679          * @param {Roo.HtmlEditorCore} this
20680          */
20681         activate: true,
20682          /**
20683          * @event beforesync
20684          * Fires before the textarea is updated with content from the editor iframe. Return false
20685          * to cancel the sync.
20686          * @param {Roo.HtmlEditorCore} this
20687          * @param {String} html
20688          */
20689         beforesync: true,
20690          /**
20691          * @event beforepush
20692          * Fires before the iframe editor is updated with content from the textarea. Return false
20693          * to cancel the push.
20694          * @param {Roo.HtmlEditorCore} this
20695          * @param {String} html
20696          */
20697         beforepush: true,
20698          /**
20699          * @event sync
20700          * Fires when the textarea is updated with content from the editor iframe.
20701          * @param {Roo.HtmlEditorCore} this
20702          * @param {String} html
20703          */
20704         sync: true,
20705          /**
20706          * @event push
20707          * Fires when the iframe editor is updated with content from the textarea.
20708          * @param {Roo.HtmlEditorCore} this
20709          * @param {String} html
20710          */
20711         push: true,
20712         
20713         /**
20714          * @event editorevent
20715          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20716          * @param {Roo.HtmlEditorCore} this
20717          */
20718         editorevent: true
20719         
20720     });
20721     
20722     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20723     
20724     // defaults : white / black...
20725     this.applyBlacklists();
20726     
20727     
20728     
20729 };
20730
20731
20732 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20733
20734
20735      /**
20736      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20737      */
20738     
20739     owner : false,
20740     
20741      /**
20742      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20743      *                        Roo.resizable.
20744      */
20745     resizable : false,
20746      /**
20747      * @cfg {Number} height (in pixels)
20748      */   
20749     height: 300,
20750    /**
20751      * @cfg {Number} width (in pixels)
20752      */   
20753     width: 500,
20754     
20755     /**
20756      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20757      * 
20758      */
20759     stylesheets: false,
20760     
20761     // id of frame..
20762     frameId: false,
20763     
20764     // private properties
20765     validationEvent : false,
20766     deferHeight: true,
20767     initialized : false,
20768     activated : false,
20769     sourceEditMode : false,
20770     onFocus : Roo.emptyFn,
20771     iframePad:3,
20772     hideMode:'offsets',
20773     
20774     clearUp: true,
20775     
20776     // blacklist + whitelisted elements..
20777     black: false,
20778     white: false,
20779      
20780     
20781
20782     /**
20783      * Protected method that will not generally be called directly. It
20784      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20785      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20786      */
20787     getDocMarkup : function(){
20788         // body styles..
20789         var st = '';
20790         
20791         // inherit styels from page...?? 
20792         if (this.stylesheets === false) {
20793             
20794             Roo.get(document.head).select('style').each(function(node) {
20795                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20796             });
20797             
20798             Roo.get(document.head).select('link').each(function(node) { 
20799                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20800             });
20801             
20802         } else if (!this.stylesheets.length) {
20803                 // simple..
20804                 st = '<style type="text/css">' +
20805                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20806                    '</style>';
20807         } else { 
20808             
20809         }
20810         
20811         st +=  '<style type="text/css">' +
20812             'IMG { cursor: pointer } ' +
20813         '</style>';
20814
20815         
20816         return '<html><head>' + st  +
20817             //<style type="text/css">' +
20818             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20819             //'</style>' +
20820             ' </head><body class="roo-htmleditor-body"></body></html>';
20821     },
20822
20823     // private
20824     onRender : function(ct, position)
20825     {
20826         var _t = this;
20827         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20828         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20829         
20830         
20831         this.el.dom.style.border = '0 none';
20832         this.el.dom.setAttribute('tabIndex', -1);
20833         this.el.addClass('x-hidden hide');
20834         
20835         
20836         
20837         if(Roo.isIE){ // fix IE 1px bogus margin
20838             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20839         }
20840        
20841         
20842         this.frameId = Roo.id();
20843         
20844          
20845         
20846         var iframe = this.owner.wrap.createChild({
20847             tag: 'iframe',
20848             cls: 'form-control', // bootstrap..
20849             id: this.frameId,
20850             name: this.frameId,
20851             frameBorder : 'no',
20852             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20853         }, this.el
20854         );
20855         
20856         
20857         this.iframe = iframe.dom;
20858
20859          this.assignDocWin();
20860         
20861         this.doc.designMode = 'on';
20862        
20863         this.doc.open();
20864         this.doc.write(this.getDocMarkup());
20865         this.doc.close();
20866
20867         
20868         var task = { // must defer to wait for browser to be ready
20869             run : function(){
20870                 //console.log("run task?" + this.doc.readyState);
20871                 this.assignDocWin();
20872                 if(this.doc.body || this.doc.readyState == 'complete'){
20873                     try {
20874                         this.doc.designMode="on";
20875                     } catch (e) {
20876                         return;
20877                     }
20878                     Roo.TaskMgr.stop(task);
20879                     this.initEditor.defer(10, this);
20880                 }
20881             },
20882             interval : 10,
20883             duration: 10000,
20884             scope: this
20885         };
20886         Roo.TaskMgr.start(task);
20887
20888     },
20889
20890     // private
20891     onResize : function(w, h)
20892     {
20893          Roo.log('resize: ' +w + ',' + h );
20894         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20895         if(!this.iframe){
20896             return;
20897         }
20898         if(typeof w == 'number'){
20899             
20900             this.iframe.style.width = w + 'px';
20901         }
20902         if(typeof h == 'number'){
20903             
20904             this.iframe.style.height = h + 'px';
20905             if(this.doc){
20906                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20907             }
20908         }
20909         
20910     },
20911
20912     /**
20913      * Toggles the editor between standard and source edit mode.
20914      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20915      */
20916     toggleSourceEdit : function(sourceEditMode){
20917         
20918         this.sourceEditMode = sourceEditMode === true;
20919         
20920         if(this.sourceEditMode){
20921  
20922             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20923             
20924         }else{
20925             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20926             //this.iframe.className = '';
20927             this.deferFocus();
20928         }
20929         //this.setSize(this.owner.wrap.getSize());
20930         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20931     },
20932
20933     
20934   
20935
20936     /**
20937      * Protected method that will not generally be called directly. If you need/want
20938      * custom HTML cleanup, this is the method you should override.
20939      * @param {String} html The HTML to be cleaned
20940      * return {String} The cleaned HTML
20941      */
20942     cleanHtml : function(html){
20943         html = String(html);
20944         if(html.length > 5){
20945             if(Roo.isSafari){ // strip safari nonsense
20946                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20947             }
20948         }
20949         if(html == '&nbsp;'){
20950             html = '';
20951         }
20952         return html;
20953     },
20954
20955     /**
20956      * HTML Editor -> Textarea
20957      * Protected method that will not generally be called directly. Syncs the contents
20958      * of the editor iframe with the textarea.
20959      */
20960     syncValue : function(){
20961         if(this.initialized){
20962             var bd = (this.doc.body || this.doc.documentElement);
20963             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20964             var html = bd.innerHTML;
20965             if(Roo.isSafari){
20966                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20967                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20968                 if(m && m[1]){
20969                     html = '<div style="'+m[0]+'">' + html + '</div>';
20970                 }
20971             }
20972             html = this.cleanHtml(html);
20973             // fix up the special chars.. normaly like back quotes in word...
20974             // however we do not want to do this with chinese..
20975             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20976                 var cc = b.charCodeAt();
20977                 if (
20978                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20979                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20980                     (cc >= 0xf900 && cc < 0xfb00 )
20981                 ) {
20982                         return b;
20983                 }
20984                 return "&#"+cc+";" 
20985             });
20986             if(this.owner.fireEvent('beforesync', this, html) !== false){
20987                 this.el.dom.value = html;
20988                 this.owner.fireEvent('sync', this, html);
20989             }
20990         }
20991     },
20992
20993     /**
20994      * Protected method that will not generally be called directly. Pushes the value of the textarea
20995      * into the iframe editor.
20996      */
20997     pushValue : function(){
20998         if(this.initialized){
20999             var v = this.el.dom.value.trim();
21000             
21001 //            if(v.length < 1){
21002 //                v = '&#160;';
21003 //            }
21004             
21005             if(this.owner.fireEvent('beforepush', this, v) !== false){
21006                 var d = (this.doc.body || this.doc.documentElement);
21007                 d.innerHTML = v;
21008                 this.cleanUpPaste();
21009                 this.el.dom.value = d.innerHTML;
21010                 this.owner.fireEvent('push', this, v);
21011             }
21012         }
21013     },
21014
21015     // private
21016     deferFocus : function(){
21017         this.focus.defer(10, this);
21018     },
21019
21020     // doc'ed in Field
21021     focus : function(){
21022         if(this.win && !this.sourceEditMode){
21023             this.win.focus();
21024         }else{
21025             this.el.focus();
21026         }
21027     },
21028     
21029     assignDocWin: function()
21030     {
21031         var iframe = this.iframe;
21032         
21033          if(Roo.isIE){
21034             this.doc = iframe.contentWindow.document;
21035             this.win = iframe.contentWindow;
21036         } else {
21037 //            if (!Roo.get(this.frameId)) {
21038 //                return;
21039 //            }
21040 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21041 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21042             
21043             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21044                 return;
21045             }
21046             
21047             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21048             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21049         }
21050     },
21051     
21052     // private
21053     initEditor : function(){
21054         //console.log("INIT EDITOR");
21055         this.assignDocWin();
21056         
21057         
21058         
21059         this.doc.designMode="on";
21060         this.doc.open();
21061         this.doc.write(this.getDocMarkup());
21062         this.doc.close();
21063         
21064         var dbody = (this.doc.body || this.doc.documentElement);
21065         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21066         // this copies styles from the containing element into thsi one..
21067         // not sure why we need all of this..
21068         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21069         
21070         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21071         //ss['background-attachment'] = 'fixed'; // w3c
21072         dbody.bgProperties = 'fixed'; // ie
21073         //Roo.DomHelper.applyStyles(dbody, ss);
21074         Roo.EventManager.on(this.doc, {
21075             //'mousedown': this.onEditorEvent,
21076             'mouseup': this.onEditorEvent,
21077             'dblclick': this.onEditorEvent,
21078             'click': this.onEditorEvent,
21079             'keyup': this.onEditorEvent,
21080             buffer:100,
21081             scope: this
21082         });
21083         if(Roo.isGecko){
21084             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21085         }
21086         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21087             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21088         }
21089         this.initialized = true;
21090
21091         this.owner.fireEvent('initialize', this);
21092         this.pushValue();
21093     },
21094
21095     // private
21096     onDestroy : function(){
21097         
21098         
21099         
21100         if(this.rendered){
21101             
21102             //for (var i =0; i < this.toolbars.length;i++) {
21103             //    // fixme - ask toolbars for heights?
21104             //    this.toolbars[i].onDestroy();
21105            // }
21106             
21107             //this.wrap.dom.innerHTML = '';
21108             //this.wrap.remove();
21109         }
21110     },
21111
21112     // private
21113     onFirstFocus : function(){
21114         
21115         this.assignDocWin();
21116         
21117         
21118         this.activated = true;
21119          
21120     
21121         if(Roo.isGecko){ // prevent silly gecko errors
21122             this.win.focus();
21123             var s = this.win.getSelection();
21124             if(!s.focusNode || s.focusNode.nodeType != 3){
21125                 var r = s.getRangeAt(0);
21126                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21127                 r.collapse(true);
21128                 this.deferFocus();
21129             }
21130             try{
21131                 this.execCmd('useCSS', true);
21132                 this.execCmd('styleWithCSS', false);
21133             }catch(e){}
21134         }
21135         this.owner.fireEvent('activate', this);
21136     },
21137
21138     // private
21139     adjustFont: function(btn){
21140         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21141         //if(Roo.isSafari){ // safari
21142         //    adjust *= 2;
21143        // }
21144         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21145         if(Roo.isSafari){ // safari
21146             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21147             v =  (v < 10) ? 10 : v;
21148             v =  (v > 48) ? 48 : v;
21149             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21150             
21151         }
21152         
21153         
21154         v = Math.max(1, v+adjust);
21155         
21156         this.execCmd('FontSize', v  );
21157     },
21158
21159     onEditorEvent : function(e)
21160     {
21161         this.owner.fireEvent('editorevent', this, e);
21162       //  this.updateToolbar();
21163         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21164     },
21165
21166     insertTag : function(tg)
21167     {
21168         // could be a bit smarter... -> wrap the current selected tRoo..
21169         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21170             
21171             range = this.createRange(this.getSelection());
21172             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21173             wrappingNode.appendChild(range.extractContents());
21174             range.insertNode(wrappingNode);
21175
21176             return;
21177             
21178             
21179             
21180         }
21181         this.execCmd("formatblock",   tg);
21182         
21183     },
21184     
21185     insertText : function(txt)
21186     {
21187         
21188         
21189         var range = this.createRange();
21190         range.deleteContents();
21191                //alert(Sender.getAttribute('label'));
21192                
21193         range.insertNode(this.doc.createTextNode(txt));
21194     } ,
21195     
21196      
21197
21198     /**
21199      * Executes a Midas editor command on the editor document and performs necessary focus and
21200      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21201      * @param {String} cmd The Midas command
21202      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21203      */
21204     relayCmd : function(cmd, value){
21205         this.win.focus();
21206         this.execCmd(cmd, value);
21207         this.owner.fireEvent('editorevent', this);
21208         //this.updateToolbar();
21209         this.owner.deferFocus();
21210     },
21211
21212     /**
21213      * Executes a Midas editor command directly on the editor document.
21214      * For visual commands, you should use {@link #relayCmd} instead.
21215      * <b>This should only be called after the editor is initialized.</b>
21216      * @param {String} cmd The Midas command
21217      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21218      */
21219     execCmd : function(cmd, value){
21220         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21221         this.syncValue();
21222     },
21223  
21224  
21225    
21226     /**
21227      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21228      * to insert tRoo.
21229      * @param {String} text | dom node.. 
21230      */
21231     insertAtCursor : function(text)
21232     {
21233         
21234         
21235         
21236         if(!this.activated){
21237             return;
21238         }
21239         /*
21240         if(Roo.isIE){
21241             this.win.focus();
21242             var r = this.doc.selection.createRange();
21243             if(r){
21244                 r.collapse(true);
21245                 r.pasteHTML(text);
21246                 this.syncValue();
21247                 this.deferFocus();
21248             
21249             }
21250             return;
21251         }
21252         */
21253         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21254             this.win.focus();
21255             
21256             
21257             // from jquery ui (MIT licenced)
21258             var range, node;
21259             var win = this.win;
21260             
21261             if (win.getSelection && win.getSelection().getRangeAt) {
21262                 range = win.getSelection().getRangeAt(0);
21263                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21264                 range.insertNode(node);
21265             } else if (win.document.selection && win.document.selection.createRange) {
21266                 // no firefox support
21267                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21268                 win.document.selection.createRange().pasteHTML(txt);
21269             } else {
21270                 // no firefox support
21271                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21272                 this.execCmd('InsertHTML', txt);
21273             } 
21274             
21275             this.syncValue();
21276             
21277             this.deferFocus();
21278         }
21279     },
21280  // private
21281     mozKeyPress : function(e){
21282         if(e.ctrlKey){
21283             var c = e.getCharCode(), cmd;
21284           
21285             if(c > 0){
21286                 c = String.fromCharCode(c).toLowerCase();
21287                 switch(c){
21288                     case 'b':
21289                         cmd = 'bold';
21290                         break;
21291                     case 'i':
21292                         cmd = 'italic';
21293                         break;
21294                     
21295                     case 'u':
21296                         cmd = 'underline';
21297                         break;
21298                     
21299                     case 'v':
21300                         this.cleanUpPaste.defer(100, this);
21301                         return;
21302                         
21303                 }
21304                 if(cmd){
21305                     this.win.focus();
21306                     this.execCmd(cmd);
21307                     this.deferFocus();
21308                     e.preventDefault();
21309                 }
21310                 
21311             }
21312         }
21313     },
21314
21315     // private
21316     fixKeys : function(){ // load time branching for fastest keydown performance
21317         if(Roo.isIE){
21318             return function(e){
21319                 var k = e.getKey(), r;
21320                 if(k == e.TAB){
21321                     e.stopEvent();
21322                     r = this.doc.selection.createRange();
21323                     if(r){
21324                         r.collapse(true);
21325                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21326                         this.deferFocus();
21327                     }
21328                     return;
21329                 }
21330                 
21331                 if(k == e.ENTER){
21332                     r = this.doc.selection.createRange();
21333                     if(r){
21334                         var target = r.parentElement();
21335                         if(!target || target.tagName.toLowerCase() != 'li'){
21336                             e.stopEvent();
21337                             r.pasteHTML('<br />');
21338                             r.collapse(false);
21339                             r.select();
21340                         }
21341                     }
21342                 }
21343                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21344                     this.cleanUpPaste.defer(100, this);
21345                     return;
21346                 }
21347                 
21348                 
21349             };
21350         }else if(Roo.isOpera){
21351             return function(e){
21352                 var k = e.getKey();
21353                 if(k == e.TAB){
21354                     e.stopEvent();
21355                     this.win.focus();
21356                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21357                     this.deferFocus();
21358                 }
21359                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21360                     this.cleanUpPaste.defer(100, this);
21361                     return;
21362                 }
21363                 
21364             };
21365         }else if(Roo.isSafari){
21366             return function(e){
21367                 var k = e.getKey();
21368                 
21369                 if(k == e.TAB){
21370                     e.stopEvent();
21371                     this.execCmd('InsertText','\t');
21372                     this.deferFocus();
21373                     return;
21374                 }
21375                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21376                     this.cleanUpPaste.defer(100, this);
21377                     return;
21378                 }
21379                 
21380              };
21381         }
21382     }(),
21383     
21384     getAllAncestors: function()
21385     {
21386         var p = this.getSelectedNode();
21387         var a = [];
21388         if (!p) {
21389             a.push(p); // push blank onto stack..
21390             p = this.getParentElement();
21391         }
21392         
21393         
21394         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21395             a.push(p);
21396             p = p.parentNode;
21397         }
21398         a.push(this.doc.body);
21399         return a;
21400     },
21401     lastSel : false,
21402     lastSelNode : false,
21403     
21404     
21405     getSelection : function() 
21406     {
21407         this.assignDocWin();
21408         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21409     },
21410     
21411     getSelectedNode: function() 
21412     {
21413         // this may only work on Gecko!!!
21414         
21415         // should we cache this!!!!
21416         
21417         
21418         
21419          
21420         var range = this.createRange(this.getSelection()).cloneRange();
21421         
21422         if (Roo.isIE) {
21423             var parent = range.parentElement();
21424             while (true) {
21425                 var testRange = range.duplicate();
21426                 testRange.moveToElementText(parent);
21427                 if (testRange.inRange(range)) {
21428                     break;
21429                 }
21430                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21431                     break;
21432                 }
21433                 parent = parent.parentElement;
21434             }
21435             return parent;
21436         }
21437         
21438         // is ancestor a text element.
21439         var ac =  range.commonAncestorContainer;
21440         if (ac.nodeType == 3) {
21441             ac = ac.parentNode;
21442         }
21443         
21444         var ar = ac.childNodes;
21445          
21446         var nodes = [];
21447         var other_nodes = [];
21448         var has_other_nodes = false;
21449         for (var i=0;i<ar.length;i++) {
21450             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21451                 continue;
21452             }
21453             // fullly contained node.
21454             
21455             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21456                 nodes.push(ar[i]);
21457                 continue;
21458             }
21459             
21460             // probably selected..
21461             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21462                 other_nodes.push(ar[i]);
21463                 continue;
21464             }
21465             // outer..
21466             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21467                 continue;
21468             }
21469             
21470             
21471             has_other_nodes = true;
21472         }
21473         if (!nodes.length && other_nodes.length) {
21474             nodes= other_nodes;
21475         }
21476         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21477             return false;
21478         }
21479         
21480         return nodes[0];
21481     },
21482     createRange: function(sel)
21483     {
21484         // this has strange effects when using with 
21485         // top toolbar - not sure if it's a great idea.
21486         //this.editor.contentWindow.focus();
21487         if (typeof sel != "undefined") {
21488             try {
21489                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21490             } catch(e) {
21491                 return this.doc.createRange();
21492             }
21493         } else {
21494             return this.doc.createRange();
21495         }
21496     },
21497     getParentElement: function()
21498     {
21499         
21500         this.assignDocWin();
21501         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21502         
21503         var range = this.createRange(sel);
21504          
21505         try {
21506             var p = range.commonAncestorContainer;
21507             while (p.nodeType == 3) { // text node
21508                 p = p.parentNode;
21509             }
21510             return p;
21511         } catch (e) {
21512             return null;
21513         }
21514     
21515     },
21516     /***
21517      *
21518      * Range intersection.. the hard stuff...
21519      *  '-1' = before
21520      *  '0' = hits..
21521      *  '1' = after.
21522      *         [ -- selected range --- ]
21523      *   [fail]                        [fail]
21524      *
21525      *    basically..
21526      *      if end is before start or  hits it. fail.
21527      *      if start is after end or hits it fail.
21528      *
21529      *   if either hits (but other is outside. - then it's not 
21530      *   
21531      *    
21532      **/
21533     
21534     
21535     // @see http://www.thismuchiknow.co.uk/?p=64.
21536     rangeIntersectsNode : function(range, node)
21537     {
21538         var nodeRange = node.ownerDocument.createRange();
21539         try {
21540             nodeRange.selectNode(node);
21541         } catch (e) {
21542             nodeRange.selectNodeContents(node);
21543         }
21544     
21545         var rangeStartRange = range.cloneRange();
21546         rangeStartRange.collapse(true);
21547     
21548         var rangeEndRange = range.cloneRange();
21549         rangeEndRange.collapse(false);
21550     
21551         var nodeStartRange = nodeRange.cloneRange();
21552         nodeStartRange.collapse(true);
21553     
21554         var nodeEndRange = nodeRange.cloneRange();
21555         nodeEndRange.collapse(false);
21556     
21557         return rangeStartRange.compareBoundaryPoints(
21558                  Range.START_TO_START, nodeEndRange) == -1 &&
21559                rangeEndRange.compareBoundaryPoints(
21560                  Range.START_TO_START, nodeStartRange) == 1;
21561         
21562          
21563     },
21564     rangeCompareNode : function(range, node)
21565     {
21566         var nodeRange = node.ownerDocument.createRange();
21567         try {
21568             nodeRange.selectNode(node);
21569         } catch (e) {
21570             nodeRange.selectNodeContents(node);
21571         }
21572         
21573         
21574         range.collapse(true);
21575     
21576         nodeRange.collapse(true);
21577      
21578         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21579         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21580          
21581         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21582         
21583         var nodeIsBefore   =  ss == 1;
21584         var nodeIsAfter    = ee == -1;
21585         
21586         if (nodeIsBefore && nodeIsAfter) {
21587             return 0; // outer
21588         }
21589         if (!nodeIsBefore && nodeIsAfter) {
21590             return 1; //right trailed.
21591         }
21592         
21593         if (nodeIsBefore && !nodeIsAfter) {
21594             return 2;  // left trailed.
21595         }
21596         // fully contined.
21597         return 3;
21598     },
21599
21600     // private? - in a new class?
21601     cleanUpPaste :  function()
21602     {
21603         // cleans up the whole document..
21604         Roo.log('cleanuppaste');
21605         
21606         this.cleanUpChildren(this.doc.body);
21607         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21608         if (clean != this.doc.body.innerHTML) {
21609             this.doc.body.innerHTML = clean;
21610         }
21611         
21612     },
21613     
21614     cleanWordChars : function(input) {// change the chars to hex code
21615         var he = Roo.HtmlEditorCore;
21616         
21617         var output = input;
21618         Roo.each(he.swapCodes, function(sw) { 
21619             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21620             
21621             output = output.replace(swapper, sw[1]);
21622         });
21623         
21624         return output;
21625     },
21626     
21627     
21628     cleanUpChildren : function (n)
21629     {
21630         if (!n.childNodes.length) {
21631             return;
21632         }
21633         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21634            this.cleanUpChild(n.childNodes[i]);
21635         }
21636     },
21637     
21638     
21639         
21640     
21641     cleanUpChild : function (node)
21642     {
21643         var ed = this;
21644         //console.log(node);
21645         if (node.nodeName == "#text") {
21646             // clean up silly Windows -- stuff?
21647             return; 
21648         }
21649         if (node.nodeName == "#comment") {
21650             node.parentNode.removeChild(node);
21651             // clean up silly Windows -- stuff?
21652             return; 
21653         }
21654         var lcname = node.tagName.toLowerCase();
21655         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21656         // whitelist of tags..
21657         
21658         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21659             // remove node.
21660             node.parentNode.removeChild(node);
21661             return;
21662             
21663         }
21664         
21665         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21666         
21667         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21668         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21669         
21670         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21671         //    remove_keep_children = true;
21672         //}
21673         
21674         if (remove_keep_children) {
21675             this.cleanUpChildren(node);
21676             // inserts everything just before this node...
21677             while (node.childNodes.length) {
21678                 var cn = node.childNodes[0];
21679                 node.removeChild(cn);
21680                 node.parentNode.insertBefore(cn, node);
21681             }
21682             node.parentNode.removeChild(node);
21683             return;
21684         }
21685         
21686         if (!node.attributes || !node.attributes.length) {
21687             this.cleanUpChildren(node);
21688             return;
21689         }
21690         
21691         function cleanAttr(n,v)
21692         {
21693             
21694             if (v.match(/^\./) || v.match(/^\//)) {
21695                 return;
21696             }
21697             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21698                 return;
21699             }
21700             if (v.match(/^#/)) {
21701                 return;
21702             }
21703 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21704             node.removeAttribute(n);
21705             
21706         }
21707         
21708         var cwhite = this.cwhite;
21709         var cblack = this.cblack;
21710             
21711         function cleanStyle(n,v)
21712         {
21713             if (v.match(/expression/)) { //XSS?? should we even bother..
21714                 node.removeAttribute(n);
21715                 return;
21716             }
21717             
21718             var parts = v.split(/;/);
21719             var clean = [];
21720             
21721             Roo.each(parts, function(p) {
21722                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21723                 if (!p.length) {
21724                     return true;
21725                 }
21726                 var l = p.split(':').shift().replace(/\s+/g,'');
21727                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21728                 
21729                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21730 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21731                     //node.removeAttribute(n);
21732                     return true;
21733                 }
21734                 //Roo.log()
21735                 // only allow 'c whitelisted system attributes'
21736                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21737 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21738                     //node.removeAttribute(n);
21739                     return true;
21740                 }
21741                 
21742                 
21743                  
21744                 
21745                 clean.push(p);
21746                 return true;
21747             });
21748             if (clean.length) { 
21749                 node.setAttribute(n, clean.join(';'));
21750             } else {
21751                 node.removeAttribute(n);
21752             }
21753             
21754         }
21755         
21756         
21757         for (var i = node.attributes.length-1; i > -1 ; i--) {
21758             var a = node.attributes[i];
21759             //console.log(a);
21760             
21761             if (a.name.toLowerCase().substr(0,2)=='on')  {
21762                 node.removeAttribute(a.name);
21763                 continue;
21764             }
21765             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21766                 node.removeAttribute(a.name);
21767                 continue;
21768             }
21769             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21770                 cleanAttr(a.name,a.value); // fixme..
21771                 continue;
21772             }
21773             if (a.name == 'style') {
21774                 cleanStyle(a.name,a.value);
21775                 continue;
21776             }
21777             /// clean up MS crap..
21778             // tecnically this should be a list of valid class'es..
21779             
21780             
21781             if (a.name == 'class') {
21782                 if (a.value.match(/^Mso/)) {
21783                     node.className = '';
21784                 }
21785                 
21786                 if (a.value.match(/body/)) {
21787                     node.className = '';
21788                 }
21789                 continue;
21790             }
21791             
21792             // style cleanup!?
21793             // class cleanup?
21794             
21795         }
21796         
21797         
21798         this.cleanUpChildren(node);
21799         
21800         
21801     },
21802     
21803     /**
21804      * Clean up MS wordisms...
21805      */
21806     cleanWord : function(node)
21807     {
21808         
21809         
21810         if (!node) {
21811             this.cleanWord(this.doc.body);
21812             return;
21813         }
21814         if (node.nodeName == "#text") {
21815             // clean up silly Windows -- stuff?
21816             return; 
21817         }
21818         if (node.nodeName == "#comment") {
21819             node.parentNode.removeChild(node);
21820             // clean up silly Windows -- stuff?
21821             return; 
21822         }
21823         
21824         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21825             node.parentNode.removeChild(node);
21826             return;
21827         }
21828         
21829         // remove - but keep children..
21830         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21831             while (node.childNodes.length) {
21832                 var cn = node.childNodes[0];
21833                 node.removeChild(cn);
21834                 node.parentNode.insertBefore(cn, node);
21835             }
21836             node.parentNode.removeChild(node);
21837             this.iterateChildren(node, this.cleanWord);
21838             return;
21839         }
21840         // clean styles
21841         if (node.className.length) {
21842             
21843             var cn = node.className.split(/\W+/);
21844             var cna = [];
21845             Roo.each(cn, function(cls) {
21846                 if (cls.match(/Mso[a-zA-Z]+/)) {
21847                     return;
21848                 }
21849                 cna.push(cls);
21850             });
21851             node.className = cna.length ? cna.join(' ') : '';
21852             if (!cna.length) {
21853                 node.removeAttribute("class");
21854             }
21855         }
21856         
21857         if (node.hasAttribute("lang")) {
21858             node.removeAttribute("lang");
21859         }
21860         
21861         if (node.hasAttribute("style")) {
21862             
21863             var styles = node.getAttribute("style").split(";");
21864             var nstyle = [];
21865             Roo.each(styles, function(s) {
21866                 if (!s.match(/:/)) {
21867                     return;
21868                 }
21869                 var kv = s.split(":");
21870                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21871                     return;
21872                 }
21873                 // what ever is left... we allow.
21874                 nstyle.push(s);
21875             });
21876             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21877             if (!nstyle.length) {
21878                 node.removeAttribute('style');
21879             }
21880         }
21881         this.iterateChildren(node, this.cleanWord);
21882         
21883         
21884         
21885     },
21886     /**
21887      * iterateChildren of a Node, calling fn each time, using this as the scole..
21888      * @param {DomNode} node node to iterate children of.
21889      * @param {Function} fn method of this class to call on each item.
21890      */
21891     iterateChildren : function(node, fn)
21892     {
21893         if (!node.childNodes.length) {
21894                 return;
21895         }
21896         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21897            fn.call(this, node.childNodes[i])
21898         }
21899     },
21900     
21901     
21902     /**
21903      * cleanTableWidths.
21904      *
21905      * Quite often pasting from word etc.. results in tables with column and widths.
21906      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21907      *
21908      */
21909     cleanTableWidths : function(node)
21910     {
21911          
21912          
21913         if (!node) {
21914             this.cleanTableWidths(this.doc.body);
21915             return;
21916         }
21917         
21918         // ignore list...
21919         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21920             return; 
21921         }
21922         Roo.log(node.tagName);
21923         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21924             this.iterateChildren(node, this.cleanTableWidths);
21925             return;
21926         }
21927         if (node.hasAttribute('width')) {
21928             node.removeAttribute('width');
21929         }
21930         
21931          
21932         if (node.hasAttribute("style")) {
21933             // pretty basic...
21934             
21935             var styles = node.getAttribute("style").split(";");
21936             var nstyle = [];
21937             Roo.each(styles, function(s) {
21938                 if (!s.match(/:/)) {
21939                     return;
21940                 }
21941                 var kv = s.split(":");
21942                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21943                     return;
21944                 }
21945                 // what ever is left... we allow.
21946                 nstyle.push(s);
21947             });
21948             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21949             if (!nstyle.length) {
21950                 node.removeAttribute('style');
21951             }
21952         }
21953         
21954         this.iterateChildren(node, this.cleanTableWidths);
21955         
21956         
21957     },
21958     
21959     
21960     
21961     
21962     domToHTML : function(currentElement, depth, nopadtext) {
21963         
21964         depth = depth || 0;
21965         nopadtext = nopadtext || false;
21966     
21967         if (!currentElement) {
21968             return this.domToHTML(this.doc.body);
21969         }
21970         
21971         //Roo.log(currentElement);
21972         var j;
21973         var allText = false;
21974         var nodeName = currentElement.nodeName;
21975         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21976         
21977         if  (nodeName == '#text') {
21978             
21979             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21980         }
21981         
21982         
21983         var ret = '';
21984         if (nodeName != 'BODY') {
21985              
21986             var i = 0;
21987             // Prints the node tagName, such as <A>, <IMG>, etc
21988             if (tagName) {
21989                 var attr = [];
21990                 for(i = 0; i < currentElement.attributes.length;i++) {
21991                     // quoting?
21992                     var aname = currentElement.attributes.item(i).name;
21993                     if (!currentElement.attributes.item(i).value.length) {
21994                         continue;
21995                     }
21996                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21997                 }
21998                 
21999                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22000             } 
22001             else {
22002                 
22003                 // eack
22004             }
22005         } else {
22006             tagName = false;
22007         }
22008         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22009             return ret;
22010         }
22011         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22012             nopadtext = true;
22013         }
22014         
22015         
22016         // Traverse the tree
22017         i = 0;
22018         var currentElementChild = currentElement.childNodes.item(i);
22019         var allText = true;
22020         var innerHTML  = '';
22021         lastnode = '';
22022         while (currentElementChild) {
22023             // Formatting code (indent the tree so it looks nice on the screen)
22024             var nopad = nopadtext;
22025             if (lastnode == 'SPAN') {
22026                 nopad  = true;
22027             }
22028             // text
22029             if  (currentElementChild.nodeName == '#text') {
22030                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22031                 toadd = nopadtext ? toadd : toadd.trim();
22032                 if (!nopad && toadd.length > 80) {
22033                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22034                 }
22035                 innerHTML  += toadd;
22036                 
22037                 i++;
22038                 currentElementChild = currentElement.childNodes.item(i);
22039                 lastNode = '';
22040                 continue;
22041             }
22042             allText = false;
22043             
22044             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22045                 
22046             // Recursively traverse the tree structure of the child node
22047             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22048             lastnode = currentElementChild.nodeName;
22049             i++;
22050             currentElementChild=currentElement.childNodes.item(i);
22051         }
22052         
22053         ret += innerHTML;
22054         
22055         if (!allText) {
22056                 // The remaining code is mostly for formatting the tree
22057             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22058         }
22059         
22060         
22061         if (tagName) {
22062             ret+= "</"+tagName+">";
22063         }
22064         return ret;
22065         
22066     },
22067         
22068     applyBlacklists : function()
22069     {
22070         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22071         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22072         
22073         this.white = [];
22074         this.black = [];
22075         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22076             if (b.indexOf(tag) > -1) {
22077                 return;
22078             }
22079             this.white.push(tag);
22080             
22081         }, this);
22082         
22083         Roo.each(w, function(tag) {
22084             if (b.indexOf(tag) > -1) {
22085                 return;
22086             }
22087             if (this.white.indexOf(tag) > -1) {
22088                 return;
22089             }
22090             this.white.push(tag);
22091             
22092         }, this);
22093         
22094         
22095         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22096             if (w.indexOf(tag) > -1) {
22097                 return;
22098             }
22099             this.black.push(tag);
22100             
22101         }, this);
22102         
22103         Roo.each(b, function(tag) {
22104             if (w.indexOf(tag) > -1) {
22105                 return;
22106             }
22107             if (this.black.indexOf(tag) > -1) {
22108                 return;
22109             }
22110             this.black.push(tag);
22111             
22112         }, this);
22113         
22114         
22115         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22116         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22117         
22118         this.cwhite = [];
22119         this.cblack = [];
22120         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22121             if (b.indexOf(tag) > -1) {
22122                 return;
22123             }
22124             this.cwhite.push(tag);
22125             
22126         }, this);
22127         
22128         Roo.each(w, function(tag) {
22129             if (b.indexOf(tag) > -1) {
22130                 return;
22131             }
22132             if (this.cwhite.indexOf(tag) > -1) {
22133                 return;
22134             }
22135             this.cwhite.push(tag);
22136             
22137         }, this);
22138         
22139         
22140         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22141             if (w.indexOf(tag) > -1) {
22142                 return;
22143             }
22144             this.cblack.push(tag);
22145             
22146         }, this);
22147         
22148         Roo.each(b, function(tag) {
22149             if (w.indexOf(tag) > -1) {
22150                 return;
22151             }
22152             if (this.cblack.indexOf(tag) > -1) {
22153                 return;
22154             }
22155             this.cblack.push(tag);
22156             
22157         }, this);
22158     },
22159     
22160     setStylesheets : function(stylesheets)
22161     {
22162         if(typeof(stylesheets) == 'string'){
22163             Roo.get(this.iframe.contentDocument.head).createChild({
22164                 tag : 'link',
22165                 rel : 'stylesheet',
22166                 type : 'text/css',
22167                 href : stylesheets
22168             });
22169             
22170             return;
22171         }
22172         var _this = this;
22173      
22174         Roo.each(stylesheets, function(s) {
22175             if(!s.length){
22176                 return;
22177             }
22178             
22179             Roo.get(_this.iframe.contentDocument.head).createChild({
22180                 tag : 'link',
22181                 rel : 'stylesheet',
22182                 type : 'text/css',
22183                 href : s
22184             });
22185         });
22186
22187         
22188     },
22189     
22190     removeStylesheets : function()
22191     {
22192         var _this = this;
22193         
22194         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22195             s.remove();
22196         });
22197     }
22198     
22199     // hide stuff that is not compatible
22200     /**
22201      * @event blur
22202      * @hide
22203      */
22204     /**
22205      * @event change
22206      * @hide
22207      */
22208     /**
22209      * @event focus
22210      * @hide
22211      */
22212     /**
22213      * @event specialkey
22214      * @hide
22215      */
22216     /**
22217      * @cfg {String} fieldClass @hide
22218      */
22219     /**
22220      * @cfg {String} focusClass @hide
22221      */
22222     /**
22223      * @cfg {String} autoCreate @hide
22224      */
22225     /**
22226      * @cfg {String} inputType @hide
22227      */
22228     /**
22229      * @cfg {String} invalidClass @hide
22230      */
22231     /**
22232      * @cfg {String} invalidText @hide
22233      */
22234     /**
22235      * @cfg {String} msgFx @hide
22236      */
22237     /**
22238      * @cfg {String} validateOnBlur @hide
22239      */
22240 });
22241
22242 Roo.HtmlEditorCore.white = [
22243         'area', 'br', 'img', 'input', 'hr', 'wbr',
22244         
22245        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22246        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22247        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22248        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22249        'table',   'ul',         'xmp', 
22250        
22251        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22252       'thead',   'tr', 
22253      
22254       'dir', 'menu', 'ol', 'ul', 'dl',
22255        
22256       'embed',  'object'
22257 ];
22258
22259
22260 Roo.HtmlEditorCore.black = [
22261     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22262         'applet', // 
22263         'base',   'basefont', 'bgsound', 'blink',  'body', 
22264         'frame',  'frameset', 'head',    'html',   'ilayer', 
22265         'iframe', 'layer',  'link',     'meta',    'object',   
22266         'script', 'style' ,'title',  'xml' // clean later..
22267 ];
22268 Roo.HtmlEditorCore.clean = [
22269     'script', 'style', 'title', 'xml'
22270 ];
22271 Roo.HtmlEditorCore.remove = [
22272     'font'
22273 ];
22274 // attributes..
22275
22276 Roo.HtmlEditorCore.ablack = [
22277     'on'
22278 ];
22279     
22280 Roo.HtmlEditorCore.aclean = [ 
22281     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22282 ];
22283
22284 // protocols..
22285 Roo.HtmlEditorCore.pwhite= [
22286         'http',  'https',  'mailto'
22287 ];
22288
22289 // white listed style attributes.
22290 Roo.HtmlEditorCore.cwhite= [
22291       //  'text-align', /// default is to allow most things..
22292       
22293          
22294 //        'font-size'//??
22295 ];
22296
22297 // black listed style attributes.
22298 Roo.HtmlEditorCore.cblack= [
22299       //  'font-size' -- this can be set by the project 
22300 ];
22301
22302
22303 Roo.HtmlEditorCore.swapCodes   =[ 
22304     [    8211, "--" ], 
22305     [    8212, "--" ], 
22306     [    8216,  "'" ],  
22307     [    8217, "'" ],  
22308     [    8220, '"' ],  
22309     [    8221, '"' ],  
22310     [    8226, "*" ],  
22311     [    8230, "..." ]
22312 ]; 
22313
22314     /*
22315  * - LGPL
22316  *
22317  * HtmlEditor
22318  * 
22319  */
22320
22321 /**
22322  * @class Roo.bootstrap.HtmlEditor
22323  * @extends Roo.bootstrap.TextArea
22324  * Bootstrap HtmlEditor class
22325
22326  * @constructor
22327  * Create a new HtmlEditor
22328  * @param {Object} config The config object
22329  */
22330
22331 Roo.bootstrap.HtmlEditor = function(config){
22332     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22333     if (!this.toolbars) {
22334         this.toolbars = [];
22335     }
22336     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22337     this.addEvents({
22338             /**
22339              * @event initialize
22340              * Fires when the editor is fully initialized (including the iframe)
22341              * @param {HtmlEditor} this
22342              */
22343             initialize: true,
22344             /**
22345              * @event activate
22346              * Fires when the editor is first receives the focus. Any insertion must wait
22347              * until after this event.
22348              * @param {HtmlEditor} this
22349              */
22350             activate: true,
22351              /**
22352              * @event beforesync
22353              * Fires before the textarea is updated with content from the editor iframe. Return false
22354              * to cancel the sync.
22355              * @param {HtmlEditor} this
22356              * @param {String} html
22357              */
22358             beforesync: true,
22359              /**
22360              * @event beforepush
22361              * Fires before the iframe editor is updated with content from the textarea. Return false
22362              * to cancel the push.
22363              * @param {HtmlEditor} this
22364              * @param {String} html
22365              */
22366             beforepush: true,
22367              /**
22368              * @event sync
22369              * Fires when the textarea is updated with content from the editor iframe.
22370              * @param {HtmlEditor} this
22371              * @param {String} html
22372              */
22373             sync: true,
22374              /**
22375              * @event push
22376              * Fires when the iframe editor is updated with content from the textarea.
22377              * @param {HtmlEditor} this
22378              * @param {String} html
22379              */
22380             push: true,
22381              /**
22382              * @event editmodechange
22383              * Fires when the editor switches edit modes
22384              * @param {HtmlEditor} this
22385              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22386              */
22387             editmodechange: true,
22388             /**
22389              * @event editorevent
22390              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22391              * @param {HtmlEditor} this
22392              */
22393             editorevent: true,
22394             /**
22395              * @event firstfocus
22396              * Fires when on first focus - needed by toolbars..
22397              * @param {HtmlEditor} this
22398              */
22399             firstfocus: true,
22400             /**
22401              * @event autosave
22402              * Auto save the htmlEditor value as a file into Events
22403              * @param {HtmlEditor} this
22404              */
22405             autosave: true,
22406             /**
22407              * @event savedpreview
22408              * preview the saved version of htmlEditor
22409              * @param {HtmlEditor} this
22410              */
22411             savedpreview: true
22412         });
22413 };
22414
22415
22416 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22417     
22418     
22419       /**
22420      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22421      */
22422     toolbars : false,
22423    
22424      /**
22425      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22426      *                        Roo.resizable.
22427      */
22428     resizable : false,
22429      /**
22430      * @cfg {Number} height (in pixels)
22431      */   
22432     height: 300,
22433    /**
22434      * @cfg {Number} width (in pixels)
22435      */   
22436     width: false,
22437     
22438     /**
22439      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22440      * 
22441      */
22442     stylesheets: false,
22443     
22444     // id of frame..
22445     frameId: false,
22446     
22447     // private properties
22448     validationEvent : false,
22449     deferHeight: true,
22450     initialized : false,
22451     activated : false,
22452     
22453     onFocus : Roo.emptyFn,
22454     iframePad:3,
22455     hideMode:'offsets',
22456     
22457     
22458     tbContainer : false,
22459     
22460     toolbarContainer :function() {
22461         return this.wrap.select('.x-html-editor-tb',true).first();
22462     },
22463
22464     /**
22465      * Protected method that will not generally be called directly. It
22466      * is called when the editor creates its toolbar. Override this method if you need to
22467      * add custom toolbar buttons.
22468      * @param {HtmlEditor} editor
22469      */
22470     createToolbar : function(){
22471         
22472         Roo.log("create toolbars");
22473         
22474         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22475         this.toolbars[0].render(this.toolbarContainer());
22476         
22477         return;
22478         
22479 //        if (!editor.toolbars || !editor.toolbars.length) {
22480 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22481 //        }
22482 //        
22483 //        for (var i =0 ; i < editor.toolbars.length;i++) {
22484 //            editor.toolbars[i] = Roo.factory(
22485 //                    typeof(editor.toolbars[i]) == 'string' ?
22486 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
22487 //                Roo.bootstrap.HtmlEditor);
22488 //            editor.toolbars[i].init(editor);
22489 //        }
22490     },
22491
22492      
22493     // private
22494     onRender : function(ct, position)
22495     {
22496        // Roo.log("Call onRender: " + this.xtype);
22497         var _t = this;
22498         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22499       
22500         this.wrap = this.inputEl().wrap({
22501             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22502         });
22503         
22504         this.editorcore.onRender(ct, position);
22505          
22506         if (this.resizable) {
22507             this.resizeEl = new Roo.Resizable(this.wrap, {
22508                 pinned : true,
22509                 wrap: true,
22510                 dynamic : true,
22511                 minHeight : this.height,
22512                 height: this.height,
22513                 handles : this.resizable,
22514                 width: this.width,
22515                 listeners : {
22516                     resize : function(r, w, h) {
22517                         _t.onResize(w,h); // -something
22518                     }
22519                 }
22520             });
22521             
22522         }
22523         this.createToolbar(this);
22524        
22525         
22526         if(!this.width && this.resizable){
22527             this.setSize(this.wrap.getSize());
22528         }
22529         if (this.resizeEl) {
22530             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22531             // should trigger onReize..
22532         }
22533         
22534     },
22535
22536     // private
22537     onResize : function(w, h)
22538     {
22539         Roo.log('resize: ' +w + ',' + h );
22540         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22541         var ew = false;
22542         var eh = false;
22543         
22544         if(this.inputEl() ){
22545             if(typeof w == 'number'){
22546                 var aw = w - this.wrap.getFrameWidth('lr');
22547                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22548                 ew = aw;
22549             }
22550             if(typeof h == 'number'){
22551                  var tbh = -11;  // fixme it needs to tool bar size!
22552                 for (var i =0; i < this.toolbars.length;i++) {
22553                     // fixme - ask toolbars for heights?
22554                     tbh += this.toolbars[i].el.getHeight();
22555                     //if (this.toolbars[i].footer) {
22556                     //    tbh += this.toolbars[i].footer.el.getHeight();
22557                     //}
22558                 }
22559               
22560                 
22561                 
22562                 
22563                 
22564                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22565                 ah -= 5; // knock a few pixes off for look..
22566                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
22567                 var eh = ah;
22568             }
22569         }
22570         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22571         this.editorcore.onResize(ew,eh);
22572         
22573     },
22574
22575     /**
22576      * Toggles the editor between standard and source edit mode.
22577      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22578      */
22579     toggleSourceEdit : function(sourceEditMode)
22580     {
22581         this.editorcore.toggleSourceEdit(sourceEditMode);
22582         
22583         if(this.editorcore.sourceEditMode){
22584             Roo.log('editor - showing textarea');
22585             
22586 //            Roo.log('in');
22587 //            Roo.log(this.syncValue());
22588             this.syncValue();
22589             this.inputEl().removeClass(['hide', 'x-hidden']);
22590             this.inputEl().dom.removeAttribute('tabIndex');
22591             this.inputEl().focus();
22592         }else{
22593             Roo.log('editor - hiding textarea');
22594 //            Roo.log('out')
22595 //            Roo.log(this.pushValue()); 
22596             this.pushValue();
22597             
22598             this.inputEl().addClass(['hide', 'x-hidden']);
22599             this.inputEl().dom.setAttribute('tabIndex', -1);
22600             //this.deferFocus();
22601         }
22602          
22603         if(this.resizable){
22604             this.setSize(this.wrap.getSize());
22605         }
22606         
22607         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22608     },
22609  
22610     // private (for BoxComponent)
22611     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22612
22613     // private (for BoxComponent)
22614     getResizeEl : function(){
22615         return this.wrap;
22616     },
22617
22618     // private (for BoxComponent)
22619     getPositionEl : function(){
22620         return this.wrap;
22621     },
22622
22623     // private
22624     initEvents : function(){
22625         this.originalValue = this.getValue();
22626     },
22627
22628 //    /**
22629 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22630 //     * @method
22631 //     */
22632 //    markInvalid : Roo.emptyFn,
22633 //    /**
22634 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22635 //     * @method
22636 //     */
22637 //    clearInvalid : Roo.emptyFn,
22638
22639     setValue : function(v){
22640         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
22641         this.editorcore.pushValue();
22642     },
22643
22644      
22645     // private
22646     deferFocus : function(){
22647         this.focus.defer(10, this);
22648     },
22649
22650     // doc'ed in Field
22651     focus : function(){
22652         this.editorcore.focus();
22653         
22654     },
22655       
22656
22657     // private
22658     onDestroy : function(){
22659         
22660         
22661         
22662         if(this.rendered){
22663             
22664             for (var i =0; i < this.toolbars.length;i++) {
22665                 // fixme - ask toolbars for heights?
22666                 this.toolbars[i].onDestroy();
22667             }
22668             
22669             this.wrap.dom.innerHTML = '';
22670             this.wrap.remove();
22671         }
22672     },
22673
22674     // private
22675     onFirstFocus : function(){
22676         //Roo.log("onFirstFocus");
22677         this.editorcore.onFirstFocus();
22678          for (var i =0; i < this.toolbars.length;i++) {
22679             this.toolbars[i].onFirstFocus();
22680         }
22681         
22682     },
22683     
22684     // private
22685     syncValue : function()
22686     {   
22687         this.editorcore.syncValue();
22688     },
22689     
22690     pushValue : function()
22691     {   
22692         this.editorcore.pushValue();
22693     }
22694      
22695     
22696     // hide stuff that is not compatible
22697     /**
22698      * @event blur
22699      * @hide
22700      */
22701     /**
22702      * @event change
22703      * @hide
22704      */
22705     /**
22706      * @event focus
22707      * @hide
22708      */
22709     /**
22710      * @event specialkey
22711      * @hide
22712      */
22713     /**
22714      * @cfg {String} fieldClass @hide
22715      */
22716     /**
22717      * @cfg {String} focusClass @hide
22718      */
22719     /**
22720      * @cfg {String} autoCreate @hide
22721      */
22722     /**
22723      * @cfg {String} inputType @hide
22724      */
22725     /**
22726      * @cfg {String} invalidClass @hide
22727      */
22728     /**
22729      * @cfg {String} invalidText @hide
22730      */
22731     /**
22732      * @cfg {String} msgFx @hide
22733      */
22734     /**
22735      * @cfg {String} validateOnBlur @hide
22736      */
22737 });
22738  
22739     
22740    
22741    
22742    
22743       
22744 Roo.namespace('Roo.bootstrap.htmleditor');
22745 /**
22746  * @class Roo.bootstrap.HtmlEditorToolbar1
22747  * Basic Toolbar
22748  * 
22749  * Usage:
22750  *
22751  new Roo.bootstrap.HtmlEditor({
22752     ....
22753     toolbars : [
22754         new Roo.bootstrap.HtmlEditorToolbar1({
22755             disable : { fonts: 1 , format: 1, ..., ... , ...],
22756             btns : [ .... ]
22757         })
22758     }
22759      
22760  * 
22761  * @cfg {Object} disable List of elements to disable..
22762  * @cfg {Array} btns List of additional buttons.
22763  * 
22764  * 
22765  * NEEDS Extra CSS? 
22766  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22767  */
22768  
22769 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22770 {
22771     
22772     Roo.apply(this, config);
22773     
22774     // default disabled, based on 'good practice'..
22775     this.disable = this.disable || {};
22776     Roo.applyIf(this.disable, {
22777         fontSize : true,
22778         colors : true,
22779         specialElements : true
22780     });
22781     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22782     
22783     this.editor = config.editor;
22784     this.editorcore = config.editor.editorcore;
22785     
22786     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22787     
22788     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22789     // dont call parent... till later.
22790 }
22791 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
22792      
22793     bar : true,
22794     
22795     editor : false,
22796     editorcore : false,
22797     
22798     
22799     formats : [
22800         "p" ,  
22801         "h1","h2","h3","h4","h5","h6", 
22802         "pre", "code", 
22803         "abbr", "acronym", "address", "cite", "samp", "var",
22804         'div','span'
22805     ],
22806     
22807     onRender : function(ct, position)
22808     {
22809        // Roo.log("Call onRender: " + this.xtype);
22810         
22811        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22812        Roo.log(this.el);
22813        this.el.dom.style.marginBottom = '0';
22814        var _this = this;
22815        var editorcore = this.editorcore;
22816        var editor= this.editor;
22817        
22818        var children = [];
22819        var btn = function(id,cmd , toggle, handler){
22820        
22821             var  event = toggle ? 'toggle' : 'click';
22822        
22823             var a = {
22824                 size : 'sm',
22825                 xtype: 'Button',
22826                 xns: Roo.bootstrap,
22827                 glyphicon : id,
22828                 cmd : id || cmd,
22829                 enableToggle:toggle !== false,
22830                 //html : 'submit'
22831                 pressed : toggle ? false : null,
22832                 listeners : {}
22833             };
22834             a.listeners[toggle ? 'toggle' : 'click'] = function() {
22835                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
22836             };
22837             children.push(a);
22838             return a;
22839        }
22840         
22841         var style = {
22842                 xtype: 'Button',
22843                 size : 'sm',
22844                 xns: Roo.bootstrap,
22845                 glyphicon : 'font',
22846                 //html : 'submit'
22847                 menu : {
22848                     xtype: 'Menu',
22849                     xns: Roo.bootstrap,
22850                     items:  []
22851                 }
22852         };
22853         Roo.each(this.formats, function(f) {
22854             style.menu.items.push({
22855                 xtype :'MenuItem',
22856                 xns: Roo.bootstrap,
22857                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22858                 tagname : f,
22859                 listeners : {
22860                     click : function()
22861                     {
22862                         editorcore.insertTag(this.tagname);
22863                         editor.focus();
22864                     }
22865                 }
22866                 
22867             });
22868         });
22869          children.push(style);   
22870             
22871             
22872         btn('bold',false,true);
22873         btn('italic',false,true);
22874         btn('align-left', 'justifyleft',true);
22875         btn('align-center', 'justifycenter',true);
22876         btn('align-right' , 'justifyright',true);
22877         btn('link', false, false, function(btn) {
22878             //Roo.log("create link?");
22879             var url = prompt(this.createLinkText, this.defaultLinkValue);
22880             if(url && url != 'http:/'+'/'){
22881                 this.editorcore.relayCmd('createlink', url);
22882             }
22883         }),
22884         btn('list','insertunorderedlist',true);
22885         btn('pencil', false,true, function(btn){
22886                 Roo.log(this);
22887                 
22888                 this.toggleSourceEdit(btn.pressed);
22889         });
22890         /*
22891         var cog = {
22892                 xtype: 'Button',
22893                 size : 'sm',
22894                 xns: Roo.bootstrap,
22895                 glyphicon : 'cog',
22896                 //html : 'submit'
22897                 menu : {
22898                     xtype: 'Menu',
22899                     xns: Roo.bootstrap,
22900                     items:  []
22901                 }
22902         };
22903         
22904         cog.menu.items.push({
22905             xtype :'MenuItem',
22906             xns: Roo.bootstrap,
22907             html : Clean styles,
22908             tagname : f,
22909             listeners : {
22910                 click : function()
22911                 {
22912                     editorcore.insertTag(this.tagname);
22913                     editor.focus();
22914                 }
22915             }
22916             
22917         });
22918        */
22919         
22920          
22921        this.xtype = 'NavSimplebar';
22922         
22923         for(var i=0;i< children.length;i++) {
22924             
22925             this.buttons.add(this.addxtypeChild(children[i]));
22926             
22927         }
22928         
22929         editor.on('editorevent', this.updateToolbar, this);
22930     },
22931     onBtnClick : function(id)
22932     {
22933        this.editorcore.relayCmd(id);
22934        this.editorcore.focus();
22935     },
22936     
22937     /**
22938      * Protected method that will not generally be called directly. It triggers
22939      * a toolbar update by reading the markup state of the current selection in the editor.
22940      */
22941     updateToolbar: function(){
22942
22943         if(!this.editorcore.activated){
22944             this.editor.onFirstFocus(); // is this neeed?
22945             return;
22946         }
22947
22948         var btns = this.buttons; 
22949         var doc = this.editorcore.doc;
22950         btns.get('bold').setActive(doc.queryCommandState('bold'));
22951         btns.get('italic').setActive(doc.queryCommandState('italic'));
22952         //btns.get('underline').setActive(doc.queryCommandState('underline'));
22953         
22954         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22955         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22956         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22957         
22958         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22959         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22960          /*
22961         
22962         var ans = this.editorcore.getAllAncestors();
22963         if (this.formatCombo) {
22964             
22965             
22966             var store = this.formatCombo.store;
22967             this.formatCombo.setValue("");
22968             for (var i =0; i < ans.length;i++) {
22969                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22970                     // select it..
22971                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22972                     break;
22973                 }
22974             }
22975         }
22976         
22977         
22978         
22979         // hides menus... - so this cant be on a menu...
22980         Roo.bootstrap.MenuMgr.hideAll();
22981         */
22982         Roo.bootstrap.MenuMgr.hideAll();
22983         //this.editorsyncValue();
22984     },
22985     onFirstFocus: function() {
22986         this.buttons.each(function(item){
22987            item.enable();
22988         });
22989     },
22990     toggleSourceEdit : function(sourceEditMode){
22991         
22992           
22993         if(sourceEditMode){
22994             Roo.log("disabling buttons");
22995            this.buttons.each( function(item){
22996                 if(item.cmd != 'pencil'){
22997                     item.disable();
22998                 }
22999             });
23000           
23001         }else{
23002             Roo.log("enabling buttons");
23003             if(this.editorcore.initialized){
23004                 this.buttons.each( function(item){
23005                     item.enable();
23006                 });
23007             }
23008             
23009         }
23010         Roo.log("calling toggole on editor");
23011         // tell the editor that it's been pressed..
23012         this.editor.toggleSourceEdit(sourceEditMode);
23013        
23014     }
23015 });
23016
23017
23018
23019
23020
23021 /**
23022  * @class Roo.bootstrap.Table.AbstractSelectionModel
23023  * @extends Roo.util.Observable
23024  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23025  * implemented by descendant classes.  This class should not be directly instantiated.
23026  * @constructor
23027  */
23028 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23029     this.locked = false;
23030     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23031 };
23032
23033
23034 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23035     /** @ignore Called by the grid automatically. Do not call directly. */
23036     init : function(grid){
23037         this.grid = grid;
23038         this.initEvents();
23039     },
23040
23041     /**
23042      * Locks the selections.
23043      */
23044     lock : function(){
23045         this.locked = true;
23046     },
23047
23048     /**
23049      * Unlocks the selections.
23050      */
23051     unlock : function(){
23052         this.locked = false;
23053     },
23054
23055     /**
23056      * Returns true if the selections are locked.
23057      * @return {Boolean}
23058      */
23059     isLocked : function(){
23060         return this.locked;
23061     }
23062 });
23063 /**
23064  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23065  * @class Roo.bootstrap.Table.RowSelectionModel
23066  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23067  * It supports multiple selections and keyboard selection/navigation. 
23068  * @constructor
23069  * @param {Object} config
23070  */
23071
23072 Roo.bootstrap.Table.RowSelectionModel = function(config){
23073     Roo.apply(this, config);
23074     this.selections = new Roo.util.MixedCollection(false, function(o){
23075         return o.id;
23076     });
23077
23078     this.last = false;
23079     this.lastActive = false;
23080
23081     this.addEvents({
23082         /**
23083              * @event selectionchange
23084              * Fires when the selection changes
23085              * @param {SelectionModel} this
23086              */
23087             "selectionchange" : true,
23088         /**
23089              * @event afterselectionchange
23090              * Fires after the selection changes (eg. by key press or clicking)
23091              * @param {SelectionModel} this
23092              */
23093             "afterselectionchange" : true,
23094         /**
23095              * @event beforerowselect
23096              * Fires when a row is selected being selected, return false to cancel.
23097              * @param {SelectionModel} this
23098              * @param {Number} rowIndex The selected index
23099              * @param {Boolean} keepExisting False if other selections will be cleared
23100              */
23101             "beforerowselect" : true,
23102         /**
23103              * @event rowselect
23104              * Fires when a row is selected.
23105              * @param {SelectionModel} this
23106              * @param {Number} rowIndex The selected index
23107              * @param {Roo.data.Record} r The record
23108              */
23109             "rowselect" : true,
23110         /**
23111              * @event rowdeselect
23112              * Fires when a row is deselected.
23113              * @param {SelectionModel} this
23114              * @param {Number} rowIndex The selected index
23115              */
23116         "rowdeselect" : true
23117     });
23118     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23119     this.locked = false;
23120  };
23121
23122 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23123     /**
23124      * @cfg {Boolean} singleSelect
23125      * True to allow selection of only one row at a time (defaults to false)
23126      */
23127     singleSelect : false,
23128
23129     // private
23130     initEvents : function()
23131     {
23132
23133         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23134         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23135         //}else{ // allow click to work like normal
23136          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23137         //}
23138         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23139         this.grid.on("rowclick", this.handleMouseDown, this);
23140         
23141         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23142             "up" : function(e){
23143                 if(!e.shiftKey){
23144                     this.selectPrevious(e.shiftKey);
23145                 }else if(this.last !== false && this.lastActive !== false){
23146                     var last = this.last;
23147                     this.selectRange(this.last,  this.lastActive-1);
23148                     this.grid.getView().focusRow(this.lastActive);
23149                     if(last !== false){
23150                         this.last = last;
23151                     }
23152                 }else{
23153                     this.selectFirstRow();
23154                 }
23155                 this.fireEvent("afterselectionchange", this);
23156             },
23157             "down" : function(e){
23158                 if(!e.shiftKey){
23159                     this.selectNext(e.shiftKey);
23160                 }else if(this.last !== false && this.lastActive !== false){
23161                     var last = this.last;
23162                     this.selectRange(this.last,  this.lastActive+1);
23163                     this.grid.getView().focusRow(this.lastActive);
23164                     if(last !== false){
23165                         this.last = last;
23166                     }
23167                 }else{
23168                     this.selectFirstRow();
23169                 }
23170                 this.fireEvent("afterselectionchange", this);
23171             },
23172             scope: this
23173         });
23174         this.grid.store.on('load', function(){
23175             this.selections.clear();
23176         },this);
23177         /*
23178         var view = this.grid.view;
23179         view.on("refresh", this.onRefresh, this);
23180         view.on("rowupdated", this.onRowUpdated, this);
23181         view.on("rowremoved", this.onRemove, this);
23182         */
23183     },
23184
23185     // private
23186     onRefresh : function()
23187     {
23188         var ds = this.grid.store, i, v = this.grid.view;
23189         var s = this.selections;
23190         s.each(function(r){
23191             if((i = ds.indexOfId(r.id)) != -1){
23192                 v.onRowSelect(i);
23193             }else{
23194                 s.remove(r);
23195             }
23196         });
23197     },
23198
23199     // private
23200     onRemove : function(v, index, r){
23201         this.selections.remove(r);
23202     },
23203
23204     // private
23205     onRowUpdated : function(v, index, r){
23206         if(this.isSelected(r)){
23207             v.onRowSelect(index);
23208         }
23209     },
23210
23211     /**
23212      * Select records.
23213      * @param {Array} records The records to select
23214      * @param {Boolean} keepExisting (optional) True to keep existing selections
23215      */
23216     selectRecords : function(records, keepExisting)
23217     {
23218         if(!keepExisting){
23219             this.clearSelections();
23220         }
23221             var ds = this.grid.store;
23222         for(var i = 0, len = records.length; i < len; i++){
23223             this.selectRow(ds.indexOf(records[i]), true);
23224         }
23225     },
23226
23227     /**
23228      * Gets the number of selected rows.
23229      * @return {Number}
23230      */
23231     getCount : function(){
23232         return this.selections.length;
23233     },
23234
23235     /**
23236      * Selects the first row in the grid.
23237      */
23238     selectFirstRow : function(){
23239         this.selectRow(0);
23240     },
23241
23242     /**
23243      * Select the last row.
23244      * @param {Boolean} keepExisting (optional) True to keep existing selections
23245      */
23246     selectLastRow : function(keepExisting){
23247         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23248         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23249     },
23250
23251     /**
23252      * Selects the row immediately following the last selected row.
23253      * @param {Boolean} keepExisting (optional) True to keep existing selections
23254      */
23255     selectNext : function(keepExisting)
23256     {
23257             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23258             this.selectRow(this.last+1, keepExisting);
23259             this.grid.getView().focusRow(this.last);
23260         }
23261     },
23262
23263     /**
23264      * Selects the row that precedes the last selected row.
23265      * @param {Boolean} keepExisting (optional) True to keep existing selections
23266      */
23267     selectPrevious : function(keepExisting){
23268         if(this.last){
23269             this.selectRow(this.last-1, keepExisting);
23270             this.grid.getView().focusRow(this.last);
23271         }
23272     },
23273
23274     /**
23275      * Returns the selected records
23276      * @return {Array} Array of selected records
23277      */
23278     getSelections : function(){
23279         return [].concat(this.selections.items);
23280     },
23281
23282     /**
23283      * Returns the first selected record.
23284      * @return {Record}
23285      */
23286     getSelected : function(){
23287         return this.selections.itemAt(0);
23288     },
23289
23290
23291     /**
23292      * Clears all selections.
23293      */
23294     clearSelections : function(fast)
23295     {
23296         if(this.locked) {
23297             return;
23298         }
23299         if(fast !== true){
23300                 var ds = this.grid.store;
23301             var s = this.selections;
23302             s.each(function(r){
23303                 this.deselectRow(ds.indexOfId(r.id));
23304             }, this);
23305             s.clear();
23306         }else{
23307             this.selections.clear();
23308         }
23309         this.last = false;
23310     },
23311
23312
23313     /**
23314      * Selects all rows.
23315      */
23316     selectAll : function(){
23317         if(this.locked) {
23318             return;
23319         }
23320         this.selections.clear();
23321         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23322             this.selectRow(i, true);
23323         }
23324     },
23325
23326     /**
23327      * Returns True if there is a selection.
23328      * @return {Boolean}
23329      */
23330     hasSelection : function(){
23331         return this.selections.length > 0;
23332     },
23333
23334     /**
23335      * Returns True if the specified row is selected.
23336      * @param {Number/Record} record The record or index of the record to check
23337      * @return {Boolean}
23338      */
23339     isSelected : function(index){
23340             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23341         return (r && this.selections.key(r.id) ? true : false);
23342     },
23343
23344     /**
23345      * Returns True if the specified record id is selected.
23346      * @param {String} id The id of record to check
23347      * @return {Boolean}
23348      */
23349     isIdSelected : function(id){
23350         return (this.selections.key(id) ? true : false);
23351     },
23352
23353
23354     // private
23355     handleMouseDBClick : function(e, t){
23356         
23357     },
23358     // private
23359     handleMouseDown : function(e, t)
23360     {
23361             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23362         if(this.isLocked() || rowIndex < 0 ){
23363             return;
23364         };
23365         if(e.shiftKey && this.last !== false){
23366             var last = this.last;
23367             this.selectRange(last, rowIndex, e.ctrlKey);
23368             this.last = last; // reset the last
23369             t.focus();
23370     
23371         }else{
23372             var isSelected = this.isSelected(rowIndex);
23373             //Roo.log("select row:" + rowIndex);
23374             if(isSelected){
23375                 this.deselectRow(rowIndex);
23376             } else {
23377                         this.selectRow(rowIndex, true);
23378             }
23379     
23380             /*
23381                 if(e.button !== 0 && isSelected){
23382                 alert('rowIndex 2: ' + rowIndex);
23383                     view.focusRow(rowIndex);
23384                 }else if(e.ctrlKey && isSelected){
23385                     this.deselectRow(rowIndex);
23386                 }else if(!isSelected){
23387                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23388                     view.focusRow(rowIndex);
23389                 }
23390             */
23391         }
23392         this.fireEvent("afterselectionchange", this);
23393     },
23394     // private
23395     handleDragableRowClick :  function(grid, rowIndex, e) 
23396     {
23397         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23398             this.selectRow(rowIndex, false);
23399             grid.view.focusRow(rowIndex);
23400              this.fireEvent("afterselectionchange", this);
23401         }
23402     },
23403     
23404     /**
23405      * Selects multiple rows.
23406      * @param {Array} rows Array of the indexes of the row to select
23407      * @param {Boolean} keepExisting (optional) True to keep existing selections
23408      */
23409     selectRows : function(rows, keepExisting){
23410         if(!keepExisting){
23411             this.clearSelections();
23412         }
23413         for(var i = 0, len = rows.length; i < len; i++){
23414             this.selectRow(rows[i], true);
23415         }
23416     },
23417
23418     /**
23419      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23420      * @param {Number} startRow The index of the first row in the range
23421      * @param {Number} endRow The index of the last row in the range
23422      * @param {Boolean} keepExisting (optional) True to retain existing selections
23423      */
23424     selectRange : function(startRow, endRow, keepExisting){
23425         if(this.locked) {
23426             return;
23427         }
23428         if(!keepExisting){
23429             this.clearSelections();
23430         }
23431         if(startRow <= endRow){
23432             for(var i = startRow; i <= endRow; i++){
23433                 this.selectRow(i, true);
23434             }
23435         }else{
23436             for(var i = startRow; i >= endRow; i--){
23437                 this.selectRow(i, true);
23438             }
23439         }
23440     },
23441
23442     /**
23443      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23444      * @param {Number} startRow The index of the first row in the range
23445      * @param {Number} endRow The index of the last row in the range
23446      */
23447     deselectRange : function(startRow, endRow, preventViewNotify){
23448         if(this.locked) {
23449             return;
23450         }
23451         for(var i = startRow; i <= endRow; i++){
23452             this.deselectRow(i, preventViewNotify);
23453         }
23454     },
23455
23456     /**
23457      * Selects a row.
23458      * @param {Number} row The index of the row to select
23459      * @param {Boolean} keepExisting (optional) True to keep existing selections
23460      */
23461     selectRow : function(index, keepExisting, preventViewNotify)
23462     {
23463             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23464             return;
23465         }
23466         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23467             if(!keepExisting || this.singleSelect){
23468                 this.clearSelections();
23469             }
23470             
23471             var r = this.grid.store.getAt(index);
23472             //console.log('selectRow - record id :' + r.id);
23473             
23474             this.selections.add(r);
23475             this.last = this.lastActive = index;
23476             if(!preventViewNotify){
23477                 var proxy = new Roo.Element(
23478                                 this.grid.getRowDom(index)
23479                 );
23480                 proxy.addClass('bg-info info');
23481             }
23482             this.fireEvent("rowselect", this, index, r);
23483             this.fireEvent("selectionchange", this);
23484         }
23485     },
23486
23487     /**
23488      * Deselects a row.
23489      * @param {Number} row The index of the row to deselect
23490      */
23491     deselectRow : function(index, preventViewNotify)
23492     {
23493         if(this.locked) {
23494             return;
23495         }
23496         if(this.last == index){
23497             this.last = false;
23498         }
23499         if(this.lastActive == index){
23500             this.lastActive = false;
23501         }
23502         
23503         var r = this.grid.store.getAt(index);
23504         if (!r) {
23505             return;
23506         }
23507         
23508         this.selections.remove(r);
23509         //.console.log('deselectRow - record id :' + r.id);
23510         if(!preventViewNotify){
23511         
23512             var proxy = new Roo.Element(
23513                 this.grid.getRowDom(index)
23514             );
23515             proxy.removeClass('bg-info info');
23516         }
23517         this.fireEvent("rowdeselect", this, index);
23518         this.fireEvent("selectionchange", this);
23519     },
23520
23521     // private
23522     restoreLast : function(){
23523         if(this._last){
23524             this.last = this._last;
23525         }
23526     },
23527
23528     // private
23529     acceptsNav : function(row, col, cm){
23530         return !cm.isHidden(col) && cm.isCellEditable(col, row);
23531     },
23532
23533     // private
23534     onEditorKey : function(field, e){
23535         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23536         if(k == e.TAB){
23537             e.stopEvent();
23538             ed.completeEdit();
23539             if(e.shiftKey){
23540                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23541             }else{
23542                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23543             }
23544         }else if(k == e.ENTER && !e.ctrlKey){
23545             e.stopEvent();
23546             ed.completeEdit();
23547             if(e.shiftKey){
23548                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
23549             }else{
23550                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
23551             }
23552         }else if(k == e.ESC){
23553             ed.cancelEdit();
23554         }
23555         if(newCell){
23556             g.startEditing(newCell[0], newCell[1]);
23557         }
23558     }
23559 });
23560 /*
23561  * Based on:
23562  * Ext JS Library 1.1.1
23563  * Copyright(c) 2006-2007, Ext JS, LLC.
23564  *
23565  * Originally Released Under LGPL - original licence link has changed is not relivant.
23566  *
23567  * Fork - LGPL
23568  * <script type="text/javascript">
23569  */
23570  
23571 /**
23572  * @class Roo.bootstrap.PagingToolbar
23573  * @extends Roo.bootstrap.NavSimplebar
23574  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
23575  * @constructor
23576  * Create a new PagingToolbar
23577  * @param {Object} config The config object
23578  * @param {Roo.data.Store} store
23579  */
23580 Roo.bootstrap.PagingToolbar = function(config)
23581 {
23582     // old args format still supported... - xtype is prefered..
23583         // created from xtype...
23584     
23585     this.ds = config.dataSource;
23586     
23587     if (config.store && !this.ds) {
23588         this.store= Roo.factory(config.store, Roo.data);
23589         this.ds = this.store;
23590         this.ds.xmodule = this.xmodule || false;
23591     }
23592     
23593     this.toolbarItems = [];
23594     if (config.items) {
23595         this.toolbarItems = config.items;
23596     }
23597     
23598     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
23599     
23600     this.cursor = 0;
23601     
23602     if (this.ds) { 
23603         this.bind(this.ds);
23604     }
23605     
23606     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
23607     
23608 };
23609
23610 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
23611     /**
23612      * @cfg {Roo.data.Store} dataSource
23613      * The underlying data store providing the paged data
23614      */
23615     /**
23616      * @cfg {String/HTMLElement/Element} container
23617      * container The id or element that will contain the toolbar
23618      */
23619     /**
23620      * @cfg {Boolean} displayInfo
23621      * True to display the displayMsg (defaults to false)
23622      */
23623     /**
23624      * @cfg {Number} pageSize
23625      * The number of records to display per page (defaults to 20)
23626      */
23627     pageSize: 20,
23628     /**
23629      * @cfg {String} displayMsg
23630      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
23631      */
23632     displayMsg : 'Displaying {0} - {1} of {2}',
23633     /**
23634      * @cfg {String} emptyMsg
23635      * The message to display when no records are found (defaults to "No data to display")
23636      */
23637     emptyMsg : 'No data to display',
23638     /**
23639      * Customizable piece of the default paging text (defaults to "Page")
23640      * @type String
23641      */
23642     beforePageText : "Page",
23643     /**
23644      * Customizable piece of the default paging text (defaults to "of %0")
23645      * @type String
23646      */
23647     afterPageText : "of {0}",
23648     /**
23649      * Customizable piece of the default paging text (defaults to "First Page")
23650      * @type String
23651      */
23652     firstText : "First Page",
23653     /**
23654      * Customizable piece of the default paging text (defaults to "Previous Page")
23655      * @type String
23656      */
23657     prevText : "Previous Page",
23658     /**
23659      * Customizable piece of the default paging text (defaults to "Next Page")
23660      * @type String
23661      */
23662     nextText : "Next Page",
23663     /**
23664      * Customizable piece of the default paging text (defaults to "Last Page")
23665      * @type String
23666      */
23667     lastText : "Last Page",
23668     /**
23669      * Customizable piece of the default paging text (defaults to "Refresh")
23670      * @type String
23671      */
23672     refreshText : "Refresh",
23673
23674     buttons : false,
23675     // private
23676     onRender : function(ct, position) 
23677     {
23678         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
23679         this.navgroup.parentId = this.id;
23680         this.navgroup.onRender(this.el, null);
23681         // add the buttons to the navgroup
23682         
23683         if(this.displayInfo){
23684             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
23685             this.displayEl = this.el.select('.x-paging-info', true).first();
23686 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
23687 //            this.displayEl = navel.el.select('span',true).first();
23688         }
23689         
23690         var _this = this;
23691         
23692         if(this.buttons){
23693             Roo.each(_this.buttons, function(e){ // this might need to use render????
23694                Roo.factory(e).onRender(_this.el, null);
23695             });
23696         }
23697             
23698         Roo.each(_this.toolbarItems, function(e) {
23699             _this.navgroup.addItem(e);
23700         });
23701         
23702         
23703         this.first = this.navgroup.addItem({
23704             tooltip: this.firstText,
23705             cls: "prev",
23706             icon : 'fa fa-backward',
23707             disabled: true,
23708             preventDefault: true,
23709             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23710         });
23711         
23712         this.prev =  this.navgroup.addItem({
23713             tooltip: this.prevText,
23714             cls: "prev",
23715             icon : 'fa fa-step-backward',
23716             disabled: true,
23717             preventDefault: true,
23718             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
23719         });
23720     //this.addSeparator();
23721         
23722         
23723         var field = this.navgroup.addItem( {
23724             tagtype : 'span',
23725             cls : 'x-paging-position',
23726             
23727             html : this.beforePageText  +
23728                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23729                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
23730          } ); //?? escaped?
23731         
23732         this.field = field.el.select('input', true).first();
23733         this.field.on("keydown", this.onPagingKeydown, this);
23734         this.field.on("focus", function(){this.dom.select();});
23735     
23736     
23737         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
23738         //this.field.setHeight(18);
23739         //this.addSeparator();
23740         this.next = this.navgroup.addItem({
23741             tooltip: this.nextText,
23742             cls: "next",
23743             html : ' <i class="fa fa-step-forward">',
23744             disabled: true,
23745             preventDefault: true,
23746             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
23747         });
23748         this.last = this.navgroup.addItem({
23749             tooltip: this.lastText,
23750             icon : 'fa fa-forward',
23751             cls: "next",
23752             disabled: true,
23753             preventDefault: true,
23754             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
23755         });
23756     //this.addSeparator();
23757         this.loading = this.navgroup.addItem({
23758             tooltip: this.refreshText,
23759             icon: 'fa fa-refresh',
23760             preventDefault: true,
23761             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23762         });
23763         
23764     },
23765
23766     // private
23767     updateInfo : function(){
23768         if(this.displayEl){
23769             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23770             var msg = count == 0 ?
23771                 this.emptyMsg :
23772                 String.format(
23773                     this.displayMsg,
23774                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
23775                 );
23776             this.displayEl.update(msg);
23777         }
23778     },
23779
23780     // private
23781     onLoad : function(ds, r, o){
23782        this.cursor = o.params ? o.params.start : 0;
23783        var d = this.getPageData(),
23784             ap = d.activePage,
23785             ps = d.pages;
23786         
23787        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23788        this.field.dom.value = ap;
23789        this.first.setDisabled(ap == 1);
23790        this.prev.setDisabled(ap == 1);
23791        this.next.setDisabled(ap == ps);
23792        this.last.setDisabled(ap == ps);
23793        this.loading.enable();
23794        this.updateInfo();
23795     },
23796
23797     // private
23798     getPageData : function(){
23799         var total = this.ds.getTotalCount();
23800         return {
23801             total : total,
23802             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23803             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23804         };
23805     },
23806
23807     // private
23808     onLoadError : function(){
23809         this.loading.enable();
23810     },
23811
23812     // private
23813     onPagingKeydown : function(e){
23814         var k = e.getKey();
23815         var d = this.getPageData();
23816         if(k == e.RETURN){
23817             var v = this.field.dom.value, pageNum;
23818             if(!v || isNaN(pageNum = parseInt(v, 10))){
23819                 this.field.dom.value = d.activePage;
23820                 return;
23821             }
23822             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23823             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23824             e.stopEvent();
23825         }
23826         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))
23827         {
23828           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23829           this.field.dom.value = pageNum;
23830           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23831           e.stopEvent();
23832         }
23833         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23834         {
23835           var v = this.field.dom.value, pageNum; 
23836           var increment = (e.shiftKey) ? 10 : 1;
23837           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23838                 increment *= -1;
23839           }
23840           if(!v || isNaN(pageNum = parseInt(v, 10))) {
23841             this.field.dom.value = d.activePage;
23842             return;
23843           }
23844           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23845           {
23846             this.field.dom.value = parseInt(v, 10) + increment;
23847             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23848             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23849           }
23850           e.stopEvent();
23851         }
23852     },
23853
23854     // private
23855     beforeLoad : function(){
23856         if(this.loading){
23857             this.loading.disable();
23858         }
23859     },
23860
23861     // private
23862     onClick : function(which){
23863         
23864         var ds = this.ds;
23865         if (!ds) {
23866             return;
23867         }
23868         
23869         switch(which){
23870             case "first":
23871                 ds.load({params:{start: 0, limit: this.pageSize}});
23872             break;
23873             case "prev":
23874                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23875             break;
23876             case "next":
23877                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23878             break;
23879             case "last":
23880                 var total = ds.getTotalCount();
23881                 var extra = total % this.pageSize;
23882                 var lastStart = extra ? (total - extra) : total-this.pageSize;
23883                 ds.load({params:{start: lastStart, limit: this.pageSize}});
23884             break;
23885             case "refresh":
23886                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23887             break;
23888         }
23889     },
23890
23891     /**
23892      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23893      * @param {Roo.data.Store} store The data store to unbind
23894      */
23895     unbind : function(ds){
23896         ds.un("beforeload", this.beforeLoad, this);
23897         ds.un("load", this.onLoad, this);
23898         ds.un("loadexception", this.onLoadError, this);
23899         ds.un("remove", this.updateInfo, this);
23900         ds.un("add", this.updateInfo, this);
23901         this.ds = undefined;
23902     },
23903
23904     /**
23905      * Binds the paging toolbar to the specified {@link Roo.data.Store}
23906      * @param {Roo.data.Store} store The data store to bind
23907      */
23908     bind : function(ds){
23909         ds.on("beforeload", this.beforeLoad, this);
23910         ds.on("load", this.onLoad, this);
23911         ds.on("loadexception", this.onLoadError, this);
23912         ds.on("remove", this.updateInfo, this);
23913         ds.on("add", this.updateInfo, this);
23914         this.ds = ds;
23915     }
23916 });/*
23917  * - LGPL
23918  *
23919  * element
23920  * 
23921  */
23922
23923 /**
23924  * @class Roo.bootstrap.MessageBar
23925  * @extends Roo.bootstrap.Component
23926  * Bootstrap MessageBar class
23927  * @cfg {String} html contents of the MessageBar
23928  * @cfg {String} weight (info | success | warning | danger) default info
23929  * @cfg {String} beforeClass insert the bar before the given class
23930  * @cfg {Boolean} closable (true | false) default false
23931  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23932  * 
23933  * @constructor
23934  * Create a new Element
23935  * @param {Object} config The config object
23936  */
23937
23938 Roo.bootstrap.MessageBar = function(config){
23939     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23940 };
23941
23942 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
23943     
23944     html: '',
23945     weight: 'info',
23946     closable: false,
23947     fixed: false,
23948     beforeClass: 'bootstrap-sticky-wrap',
23949     
23950     getAutoCreate : function(){
23951         
23952         var cfg = {
23953             tag: 'div',
23954             cls: 'alert alert-dismissable alert-' + this.weight,
23955             cn: [
23956                 {
23957                     tag: 'span',
23958                     cls: 'message',
23959                     html: this.html || ''
23960                 }
23961             ]
23962         };
23963         
23964         if(this.fixed){
23965             cfg.cls += ' alert-messages-fixed';
23966         }
23967         
23968         if(this.closable){
23969             cfg.cn.push({
23970                 tag: 'button',
23971                 cls: 'close',
23972                 html: 'x'
23973             });
23974         }
23975         
23976         return cfg;
23977     },
23978     
23979     onRender : function(ct, position)
23980     {
23981         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23982         
23983         if(!this.el){
23984             var cfg = Roo.apply({},  this.getAutoCreate());
23985             cfg.id = Roo.id();
23986             
23987             if (this.cls) {
23988                 cfg.cls += ' ' + this.cls;
23989             }
23990             if (this.style) {
23991                 cfg.style = this.style;
23992             }
23993             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23994             
23995             this.el.setVisibilityMode(Roo.Element.DISPLAY);
23996         }
23997         
23998         this.el.select('>button.close').on('click', this.hide, this);
23999         
24000     },
24001     
24002     show : function()
24003     {
24004         if (!this.rendered) {
24005             this.render();
24006         }
24007         
24008         this.el.show();
24009         
24010         this.fireEvent('show', this);
24011         
24012     },
24013     
24014     hide : function()
24015     {
24016         if (!this.rendered) {
24017             this.render();
24018         }
24019         
24020         this.el.hide();
24021         
24022         this.fireEvent('hide', this);
24023     },
24024     
24025     update : function()
24026     {
24027 //        var e = this.el.dom.firstChild;
24028 //        
24029 //        if(this.closable){
24030 //            e = e.nextSibling;
24031 //        }
24032 //        
24033 //        e.data = this.html || '';
24034
24035         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24036     }
24037    
24038 });
24039
24040  
24041
24042      /*
24043  * - LGPL
24044  *
24045  * Graph
24046  * 
24047  */
24048
24049
24050 /**
24051  * @class Roo.bootstrap.Graph
24052  * @extends Roo.bootstrap.Component
24053  * Bootstrap Graph class
24054 > Prameters
24055  -sm {number} sm 4
24056  -md {number} md 5
24057  @cfg {String} graphtype  bar | vbar | pie
24058  @cfg {number} g_x coodinator | centre x (pie)
24059  @cfg {number} g_y coodinator | centre y (pie)
24060  @cfg {number} g_r radius (pie)
24061  @cfg {number} g_height height of the chart (respected by all elements in the set)
24062  @cfg {number} g_width width of the chart (respected by all elements in the set)
24063  @cfg {Object} title The title of the chart
24064     
24065  -{Array}  values
24066  -opts (object) options for the chart 
24067      o {
24068      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24069      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24070      o vgutter (number)
24071      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.
24072      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24073      o to
24074      o stretch (boolean)
24075      o }
24076  -opts (object) options for the pie
24077      o{
24078      o cut
24079      o startAngle (number)
24080      o endAngle (number)
24081      } 
24082  *
24083  * @constructor
24084  * Create a new Input
24085  * @param {Object} config The config object
24086  */
24087
24088 Roo.bootstrap.Graph = function(config){
24089     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24090     
24091     this.addEvents({
24092         // img events
24093         /**
24094          * @event click
24095          * The img click event for the img.
24096          * @param {Roo.EventObject} e
24097          */
24098         "click" : true
24099     });
24100 };
24101
24102 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24103     
24104     sm: 4,
24105     md: 5,
24106     graphtype: 'bar',
24107     g_height: 250,
24108     g_width: 400,
24109     g_x: 50,
24110     g_y: 50,
24111     g_r: 30,
24112     opts:{
24113         //g_colors: this.colors,
24114         g_type: 'soft',
24115         g_gutter: '20%'
24116
24117     },
24118     title : false,
24119
24120     getAutoCreate : function(){
24121         
24122         var cfg = {
24123             tag: 'div',
24124             html : null
24125         };
24126         
24127         
24128         return  cfg;
24129     },
24130
24131     onRender : function(ct,position){
24132         
24133         
24134         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24135         
24136         if (typeof(Raphael) == 'undefined') {
24137             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24138             return;
24139         }
24140         
24141         this.raphael = Raphael(this.el.dom);
24142         
24143                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24144                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24145                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24146                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24147                 /*
24148                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24149                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24150                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24151                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24152                 
24153                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24154                 r.barchart(330, 10, 300, 220, data1);
24155                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24156                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24157                 */
24158                 
24159                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24160                 // r.barchart(30, 30, 560, 250,  xdata, {
24161                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24162                 //     axis : "0 0 1 1",
24163                 //     axisxlabels :  xdata
24164                 //     //yvalues : cols,
24165                    
24166                 // });
24167 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24168 //        
24169 //        this.load(null,xdata,{
24170 //                axis : "0 0 1 1",
24171 //                axisxlabels :  xdata
24172 //                });
24173
24174     },
24175
24176     load : function(graphtype,xdata,opts)
24177     {
24178         this.raphael.clear();
24179         if(!graphtype) {
24180             graphtype = this.graphtype;
24181         }
24182         if(!opts){
24183             opts = this.opts;
24184         }
24185         var r = this.raphael,
24186             fin = function () {
24187                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24188             },
24189             fout = function () {
24190                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24191             },
24192             pfin = function() {
24193                 this.sector.stop();
24194                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24195
24196                 if (this.label) {
24197                     this.label[0].stop();
24198                     this.label[0].attr({ r: 7.5 });
24199                     this.label[1].attr({ "font-weight": 800 });
24200                 }
24201             },
24202             pfout = function() {
24203                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24204
24205                 if (this.label) {
24206                     this.label[0].animate({ r: 5 }, 500, "bounce");
24207                     this.label[1].attr({ "font-weight": 400 });
24208                 }
24209             };
24210
24211         switch(graphtype){
24212             case 'bar':
24213                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24214                 break;
24215             case 'hbar':
24216                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24217                 break;
24218             case 'pie':
24219 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24220 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24221 //            
24222                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24223                 
24224                 break;
24225
24226         }
24227         
24228         if(this.title){
24229             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24230         }
24231         
24232     },
24233     
24234     setTitle: function(o)
24235     {
24236         this.title = o;
24237     },
24238     
24239     initEvents: function() {
24240         
24241         if(!this.href){
24242             this.el.on('click', this.onClick, this);
24243         }
24244     },
24245     
24246     onClick : function(e)
24247     {
24248         Roo.log('img onclick');
24249         this.fireEvent('click', this, e);
24250     }
24251    
24252 });
24253
24254  
24255 /*
24256  * - LGPL
24257  *
24258  * numberBox
24259  * 
24260  */
24261 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24262
24263 /**
24264  * @class Roo.bootstrap.dash.NumberBox
24265  * @extends Roo.bootstrap.Component
24266  * Bootstrap NumberBox class
24267  * @cfg {String} headline Box headline
24268  * @cfg {String} content Box content
24269  * @cfg {String} icon Box icon
24270  * @cfg {String} footer Footer text
24271  * @cfg {String} fhref Footer href
24272  * 
24273  * @constructor
24274  * Create a new NumberBox
24275  * @param {Object} config The config object
24276  */
24277
24278
24279 Roo.bootstrap.dash.NumberBox = function(config){
24280     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24281     
24282 };
24283
24284 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24285     
24286     headline : '',
24287     content : '',
24288     icon : '',
24289     footer : '',
24290     fhref : '',
24291     ficon : '',
24292     
24293     getAutoCreate : function(){
24294         
24295         var cfg = {
24296             tag : 'div',
24297             cls : 'small-box ',
24298             cn : [
24299                 {
24300                     tag : 'div',
24301                     cls : 'inner',
24302                     cn :[
24303                         {
24304                             tag : 'h3',
24305                             cls : 'roo-headline',
24306                             html : this.headline
24307                         },
24308                         {
24309                             tag : 'p',
24310                             cls : 'roo-content',
24311                             html : this.content
24312                         }
24313                     ]
24314                 }
24315             ]
24316         };
24317         
24318         if(this.icon){
24319             cfg.cn.push({
24320                 tag : 'div',
24321                 cls : 'icon',
24322                 cn :[
24323                     {
24324                         tag : 'i',
24325                         cls : 'ion ' + this.icon
24326                     }
24327                 ]
24328             });
24329         }
24330         
24331         if(this.footer){
24332             var footer = {
24333                 tag : 'a',
24334                 cls : 'small-box-footer',
24335                 href : this.fhref || '#',
24336                 html : this.footer
24337             };
24338             
24339             cfg.cn.push(footer);
24340             
24341         }
24342         
24343         return  cfg;
24344     },
24345
24346     onRender : function(ct,position){
24347         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24348
24349
24350        
24351                 
24352     },
24353
24354     setHeadline: function (value)
24355     {
24356         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24357     },
24358     
24359     setFooter: function (value, href)
24360     {
24361         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24362         
24363         if(href){
24364             this.el.select('a.small-box-footer',true).first().attr('href', href);
24365         }
24366         
24367     },
24368
24369     setContent: function (value)
24370     {
24371         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24372     },
24373
24374     initEvents: function() 
24375     {   
24376         
24377     }
24378     
24379 });
24380
24381  
24382 /*
24383  * - LGPL
24384  *
24385  * TabBox
24386  * 
24387  */
24388 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24389
24390 /**
24391  * @class Roo.bootstrap.dash.TabBox
24392  * @extends Roo.bootstrap.Component
24393  * Bootstrap TabBox class
24394  * @cfg {String} title Title of the TabBox
24395  * @cfg {String} icon Icon of the TabBox
24396  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24397  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24398  * 
24399  * @constructor
24400  * Create a new TabBox
24401  * @param {Object} config The config object
24402  */
24403
24404
24405 Roo.bootstrap.dash.TabBox = function(config){
24406     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24407     this.addEvents({
24408         // raw events
24409         /**
24410          * @event addpane
24411          * When a pane is added
24412          * @param {Roo.bootstrap.dash.TabPane} pane
24413          */
24414         "addpane" : true,
24415         /**
24416          * @event activatepane
24417          * When a pane is activated
24418          * @param {Roo.bootstrap.dash.TabPane} pane
24419          */
24420         "activatepane" : true
24421         
24422          
24423     });
24424     
24425     this.panes = [];
24426 };
24427
24428 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24429
24430     title : '',
24431     icon : false,
24432     showtabs : true,
24433     tabScrollable : false,
24434     
24435     getChildContainer : function()
24436     {
24437         return this.el.select('.tab-content', true).first();
24438     },
24439     
24440     getAutoCreate : function(){
24441         
24442         var header = {
24443             tag: 'li',
24444             cls: 'pull-left header',
24445             html: this.title,
24446             cn : []
24447         };
24448         
24449         if(this.icon){
24450             header.cn.push({
24451                 tag: 'i',
24452                 cls: 'fa ' + this.icon
24453             });
24454         }
24455         
24456         var h = {
24457             tag: 'ul',
24458             cls: 'nav nav-tabs pull-right',
24459             cn: [
24460                 header
24461             ]
24462         };
24463         
24464         if(this.tabScrollable){
24465             h = {
24466                 tag: 'div',
24467                 cls: 'tab-header',
24468                 cn: [
24469                     {
24470                         tag: 'ul',
24471                         cls: 'nav nav-tabs pull-right',
24472                         cn: [
24473                             header
24474                         ]
24475                     }
24476                 ]
24477             };
24478         }
24479         
24480         var cfg = {
24481             tag: 'div',
24482             cls: 'nav-tabs-custom',
24483             cn: [
24484                 h,
24485                 {
24486                     tag: 'div',
24487                     cls: 'tab-content no-padding',
24488                     cn: []
24489                 }
24490             ]
24491         };
24492
24493         return  cfg;
24494     },
24495     initEvents : function()
24496     {
24497         //Roo.log('add add pane handler');
24498         this.on('addpane', this.onAddPane, this);
24499     },
24500      /**
24501      * Updates the box title
24502      * @param {String} html to set the title to.
24503      */
24504     setTitle : function(value)
24505     {
24506         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24507     },
24508     onAddPane : function(pane)
24509     {
24510         this.panes.push(pane);
24511         //Roo.log('addpane');
24512         //Roo.log(pane);
24513         // tabs are rendere left to right..
24514         if(!this.showtabs){
24515             return;
24516         }
24517         
24518         var ctr = this.el.select('.nav-tabs', true).first();
24519          
24520          
24521         var existing = ctr.select('.nav-tab',true);
24522         var qty = existing.getCount();;
24523         
24524         
24525         var tab = ctr.createChild({
24526             tag : 'li',
24527             cls : 'nav-tab' + (qty ? '' : ' active'),
24528             cn : [
24529                 {
24530                     tag : 'a',
24531                     href:'#',
24532                     html : pane.title
24533                 }
24534             ]
24535         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24536         pane.tab = tab;
24537         
24538         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24539         if (!qty) {
24540             pane.el.addClass('active');
24541         }
24542         
24543                 
24544     },
24545     onTabClick : function(ev,un,ob,pane)
24546     {
24547         //Roo.log('tab - prev default');
24548         ev.preventDefault();
24549         
24550         
24551         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
24552         pane.tab.addClass('active');
24553         //Roo.log(pane.title);
24554         this.getChildContainer().select('.tab-pane',true).removeClass('active');
24555         // technically we should have a deactivate event.. but maybe add later.
24556         // and it should not de-activate the selected tab...
24557         this.fireEvent('activatepane', pane);
24558         pane.el.addClass('active');
24559         pane.fireEvent('activate');
24560         
24561         
24562     },
24563     
24564     getActivePane : function()
24565     {
24566         var r = false;
24567         Roo.each(this.panes, function(p) {
24568             if(p.el.hasClass('active')){
24569                 r = p;
24570                 return false;
24571             }
24572             
24573             return;
24574         });
24575         
24576         return r;
24577     }
24578     
24579     
24580 });
24581
24582  
24583 /*
24584  * - LGPL
24585  *
24586  * Tab pane
24587  * 
24588  */
24589 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24590 /**
24591  * @class Roo.bootstrap.TabPane
24592  * @extends Roo.bootstrap.Component
24593  * Bootstrap TabPane class
24594  * @cfg {Boolean} active (false | true) Default false
24595  * @cfg {String} title title of panel
24596
24597  * 
24598  * @constructor
24599  * Create a new TabPane
24600  * @param {Object} config The config object
24601  */
24602
24603 Roo.bootstrap.dash.TabPane = function(config){
24604     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
24605     
24606     this.addEvents({
24607         // raw events
24608         /**
24609          * @event activate
24610          * When a pane is activated
24611          * @param {Roo.bootstrap.dash.TabPane} pane
24612          */
24613         "activate" : true
24614          
24615     });
24616 };
24617
24618 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
24619     
24620     active : false,
24621     title : '',
24622     
24623     // the tabBox that this is attached to.
24624     tab : false,
24625      
24626     getAutoCreate : function() 
24627     {
24628         var cfg = {
24629             tag: 'div',
24630             cls: 'tab-pane'
24631         };
24632         
24633         if(this.active){
24634             cfg.cls += ' active';
24635         }
24636         
24637         return cfg;
24638     },
24639     initEvents  : function()
24640     {
24641         //Roo.log('trigger add pane handler');
24642         this.parent().fireEvent('addpane', this)
24643     },
24644     
24645      /**
24646      * Updates the tab title 
24647      * @param {String} html to set the title to.
24648      */
24649     setTitle: function(str)
24650     {
24651         if (!this.tab) {
24652             return;
24653         }
24654         this.title = str;
24655         this.tab.select('a', true).first().dom.innerHTML = str;
24656         
24657     }
24658     
24659     
24660     
24661 });
24662
24663  
24664
24665
24666  /*
24667  * - LGPL
24668  *
24669  * menu
24670  * 
24671  */
24672 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24673
24674 /**
24675  * @class Roo.bootstrap.menu.Menu
24676  * @extends Roo.bootstrap.Component
24677  * Bootstrap Menu class - container for Menu
24678  * @cfg {String} html Text of the menu
24679  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
24680  * @cfg {String} icon Font awesome icon
24681  * @cfg {String} pos Menu align to (top | bottom) default bottom
24682  * 
24683  * 
24684  * @constructor
24685  * Create a new Menu
24686  * @param {Object} config The config object
24687  */
24688
24689
24690 Roo.bootstrap.menu.Menu = function(config){
24691     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
24692     
24693     this.addEvents({
24694         /**
24695          * @event beforeshow
24696          * Fires before this menu is displayed
24697          * @param {Roo.bootstrap.menu.Menu} this
24698          */
24699         beforeshow : true,
24700         /**
24701          * @event beforehide
24702          * Fires before this menu is hidden
24703          * @param {Roo.bootstrap.menu.Menu} this
24704          */
24705         beforehide : true,
24706         /**
24707          * @event show
24708          * Fires after this menu is displayed
24709          * @param {Roo.bootstrap.menu.Menu} this
24710          */
24711         show : true,
24712         /**
24713          * @event hide
24714          * Fires after this menu is hidden
24715          * @param {Roo.bootstrap.menu.Menu} this
24716          */
24717         hide : true,
24718         /**
24719          * @event click
24720          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24721          * @param {Roo.bootstrap.menu.Menu} this
24722          * @param {Roo.EventObject} e
24723          */
24724         click : true
24725     });
24726     
24727 };
24728
24729 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
24730     
24731     submenu : false,
24732     html : '',
24733     weight : 'default',
24734     icon : false,
24735     pos : 'bottom',
24736     
24737     
24738     getChildContainer : function() {
24739         if(this.isSubMenu){
24740             return this.el;
24741         }
24742         
24743         return this.el.select('ul.dropdown-menu', true).first();  
24744     },
24745     
24746     getAutoCreate : function()
24747     {
24748         var text = [
24749             {
24750                 tag : 'span',
24751                 cls : 'roo-menu-text',
24752                 html : this.html
24753             }
24754         ];
24755         
24756         if(this.icon){
24757             text.unshift({
24758                 tag : 'i',
24759                 cls : 'fa ' + this.icon
24760             })
24761         }
24762         
24763         
24764         var cfg = {
24765             tag : 'div',
24766             cls : 'btn-group',
24767             cn : [
24768                 {
24769                     tag : 'button',
24770                     cls : 'dropdown-button btn btn-' + this.weight,
24771                     cn : text
24772                 },
24773                 {
24774                     tag : 'button',
24775                     cls : 'dropdown-toggle btn btn-' + this.weight,
24776                     cn : [
24777                         {
24778                             tag : 'span',
24779                             cls : 'caret'
24780                         }
24781                     ]
24782                 },
24783                 {
24784                     tag : 'ul',
24785                     cls : 'dropdown-menu'
24786                 }
24787             ]
24788             
24789         };
24790         
24791         if(this.pos == 'top'){
24792             cfg.cls += ' dropup';
24793         }
24794         
24795         if(this.isSubMenu){
24796             cfg = {
24797                 tag : 'ul',
24798                 cls : 'dropdown-menu'
24799             }
24800         }
24801         
24802         return cfg;
24803     },
24804     
24805     onRender : function(ct, position)
24806     {
24807         this.isSubMenu = ct.hasClass('dropdown-submenu');
24808         
24809         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24810     },
24811     
24812     initEvents : function() 
24813     {
24814         if(this.isSubMenu){
24815             return;
24816         }
24817         
24818         this.hidden = true;
24819         
24820         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24821         this.triggerEl.on('click', this.onTriggerPress, this);
24822         
24823         this.buttonEl = this.el.select('button.dropdown-button', true).first();
24824         this.buttonEl.on('click', this.onClick, this);
24825         
24826     },
24827     
24828     list : function()
24829     {
24830         if(this.isSubMenu){
24831             return this.el;
24832         }
24833         
24834         return this.el.select('ul.dropdown-menu', true).first();
24835     },
24836     
24837     onClick : function(e)
24838     {
24839         this.fireEvent("click", this, e);
24840     },
24841     
24842     onTriggerPress  : function(e)
24843     {   
24844         if (this.isVisible()) {
24845             this.hide();
24846         } else {
24847             this.show();
24848         }
24849     },
24850     
24851     isVisible : function(){
24852         return !this.hidden;
24853     },
24854     
24855     show : function()
24856     {
24857         this.fireEvent("beforeshow", this);
24858         
24859         this.hidden = false;
24860         this.el.addClass('open');
24861         
24862         Roo.get(document).on("mouseup", this.onMouseUp, this);
24863         
24864         this.fireEvent("show", this);
24865         
24866         
24867     },
24868     
24869     hide : function()
24870     {
24871         this.fireEvent("beforehide", this);
24872         
24873         this.hidden = true;
24874         this.el.removeClass('open');
24875         
24876         Roo.get(document).un("mouseup", this.onMouseUp);
24877         
24878         this.fireEvent("hide", this);
24879     },
24880     
24881     onMouseUp : function()
24882     {
24883         this.hide();
24884     }
24885     
24886 });
24887
24888  
24889  /*
24890  * - LGPL
24891  *
24892  * menu item
24893  * 
24894  */
24895 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24896
24897 /**
24898  * @class Roo.bootstrap.menu.Item
24899  * @extends Roo.bootstrap.Component
24900  * Bootstrap MenuItem class
24901  * @cfg {Boolean} submenu (true | false) default false
24902  * @cfg {String} html text of the item
24903  * @cfg {String} href the link
24904  * @cfg {Boolean} disable (true | false) default false
24905  * @cfg {Boolean} preventDefault (true | false) default true
24906  * @cfg {String} icon Font awesome icon
24907  * @cfg {String} pos Submenu align to (left | right) default right 
24908  * 
24909  * 
24910  * @constructor
24911  * Create a new Item
24912  * @param {Object} config The config object
24913  */
24914
24915
24916 Roo.bootstrap.menu.Item = function(config){
24917     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24918     this.addEvents({
24919         /**
24920          * @event mouseover
24921          * Fires when the mouse is hovering over this menu
24922          * @param {Roo.bootstrap.menu.Item} this
24923          * @param {Roo.EventObject} e
24924          */
24925         mouseover : true,
24926         /**
24927          * @event mouseout
24928          * Fires when the mouse exits this menu
24929          * @param {Roo.bootstrap.menu.Item} this
24930          * @param {Roo.EventObject} e
24931          */
24932         mouseout : true,
24933         // raw events
24934         /**
24935          * @event click
24936          * The raw click event for the entire grid.
24937          * @param {Roo.EventObject} e
24938          */
24939         click : true
24940     });
24941 };
24942
24943 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
24944     
24945     submenu : false,
24946     href : '',
24947     html : '',
24948     preventDefault: true,
24949     disable : false,
24950     icon : false,
24951     pos : 'right',
24952     
24953     getAutoCreate : function()
24954     {
24955         var text = [
24956             {
24957                 tag : 'span',
24958                 cls : 'roo-menu-item-text',
24959                 html : this.html
24960             }
24961         ];
24962         
24963         if(this.icon){
24964             text.unshift({
24965                 tag : 'i',
24966                 cls : 'fa ' + this.icon
24967             })
24968         }
24969         
24970         var cfg = {
24971             tag : 'li',
24972             cn : [
24973                 {
24974                     tag : 'a',
24975                     href : this.href || '#',
24976                     cn : text
24977                 }
24978             ]
24979         };
24980         
24981         if(this.disable){
24982             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24983         }
24984         
24985         if(this.submenu){
24986             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24987             
24988             if(this.pos == 'left'){
24989                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24990             }
24991         }
24992         
24993         return cfg;
24994     },
24995     
24996     initEvents : function() 
24997     {
24998         this.el.on('mouseover', this.onMouseOver, this);
24999         this.el.on('mouseout', this.onMouseOut, this);
25000         
25001         this.el.select('a', true).first().on('click', this.onClick, this);
25002         
25003     },
25004     
25005     onClick : function(e)
25006     {
25007         if(this.preventDefault){
25008             e.preventDefault();
25009         }
25010         
25011         this.fireEvent("click", this, e);
25012     },
25013     
25014     onMouseOver : function(e)
25015     {
25016         if(this.submenu && this.pos == 'left'){
25017             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25018         }
25019         
25020         this.fireEvent("mouseover", this, e);
25021     },
25022     
25023     onMouseOut : function(e)
25024     {
25025         this.fireEvent("mouseout", this, e);
25026     }
25027 });
25028
25029  
25030
25031  /*
25032  * - LGPL
25033  *
25034  * menu separator
25035  * 
25036  */
25037 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25038
25039 /**
25040  * @class Roo.bootstrap.menu.Separator
25041  * @extends Roo.bootstrap.Component
25042  * Bootstrap Separator class
25043  * 
25044  * @constructor
25045  * Create a new Separator
25046  * @param {Object} config The config object
25047  */
25048
25049
25050 Roo.bootstrap.menu.Separator = function(config){
25051     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25052 };
25053
25054 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25055     
25056     getAutoCreate : function(){
25057         var cfg = {
25058             tag : 'li',
25059             cls: 'divider'
25060         };
25061         
25062         return cfg;
25063     }
25064    
25065 });
25066
25067  
25068
25069  /*
25070  * - LGPL
25071  *
25072  * Tooltip
25073  * 
25074  */
25075
25076 /**
25077  * @class Roo.bootstrap.Tooltip
25078  * Bootstrap Tooltip class
25079  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25080  * to determine which dom element triggers the tooltip.
25081  * 
25082  * It needs to add support for additional attributes like tooltip-position
25083  * 
25084  * @constructor
25085  * Create a new Toolti
25086  * @param {Object} config The config object
25087  */
25088
25089 Roo.bootstrap.Tooltip = function(config){
25090     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25091     
25092     this.alignment = Roo.bootstrap.Tooltip.alignment;
25093     
25094     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25095         this.alignment = config.alignment;
25096     }
25097     
25098 };
25099
25100 Roo.apply(Roo.bootstrap.Tooltip, {
25101     /**
25102      * @function init initialize tooltip monitoring.
25103      * @static
25104      */
25105     currentEl : false,
25106     currentTip : false,
25107     currentRegion : false,
25108     
25109     //  init : delay?
25110     
25111     init : function()
25112     {
25113         Roo.get(document).on('mouseover', this.enter ,this);
25114         Roo.get(document).on('mouseout', this.leave, this);
25115          
25116         
25117         this.currentTip = new Roo.bootstrap.Tooltip();
25118     },
25119     
25120     enter : function(ev)
25121     {
25122         var dom = ev.getTarget();
25123         
25124         //Roo.log(['enter',dom]);
25125         var el = Roo.fly(dom);
25126         if (this.currentEl) {
25127             //Roo.log(dom);
25128             //Roo.log(this.currentEl);
25129             //Roo.log(this.currentEl.contains(dom));
25130             if (this.currentEl == el) {
25131                 return;
25132             }
25133             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25134                 return;
25135             }
25136
25137         }
25138         
25139         if (this.currentTip.el) {
25140             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25141         }    
25142         //Roo.log(ev);
25143         
25144         if(!el || el.dom == document){
25145             return;
25146         }
25147         
25148         var bindEl = el;
25149         
25150         // you can not look for children, as if el is the body.. then everythign is the child..
25151         if (!el.attr('tooltip')) { //
25152             if (!el.select("[tooltip]").elements.length) {
25153                 return;
25154             }
25155             // is the mouse over this child...?
25156             bindEl = el.select("[tooltip]").first();
25157             var xy = ev.getXY();
25158             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25159                 //Roo.log("not in region.");
25160                 return;
25161             }
25162             //Roo.log("child element over..");
25163             
25164         }
25165         this.currentEl = bindEl;
25166         this.currentTip.bind(bindEl);
25167         this.currentRegion = Roo.lib.Region.getRegion(dom);
25168         this.currentTip.enter();
25169         
25170     },
25171     leave : function(ev)
25172     {
25173         var dom = ev.getTarget();
25174         //Roo.log(['leave',dom]);
25175         if (!this.currentEl) {
25176             return;
25177         }
25178         
25179         
25180         if (dom != this.currentEl.dom) {
25181             return;
25182         }
25183         var xy = ev.getXY();
25184         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25185             return;
25186         }
25187         // only activate leave if mouse cursor is outside... bounding box..
25188         
25189         
25190         
25191         
25192         if (this.currentTip) {
25193             this.currentTip.leave();
25194         }
25195         //Roo.log('clear currentEl');
25196         this.currentEl = false;
25197         
25198         
25199     },
25200     alignment : {
25201         'left' : ['r-l', [-2,0], 'right'],
25202         'right' : ['l-r', [2,0], 'left'],
25203         'bottom' : ['t-b', [0,2], 'top'],
25204         'top' : [ 'b-t', [0,-2], 'bottom']
25205     }
25206     
25207 });
25208
25209
25210 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25211     
25212     
25213     bindEl : false,
25214     
25215     delay : null, // can be { show : 300 , hide: 500}
25216     
25217     timeout : null,
25218     
25219     hoverState : null, //???
25220     
25221     placement : 'bottom', 
25222     
25223     alignment : false,
25224     
25225     getAutoCreate : function(){
25226     
25227         var cfg = {
25228            cls : 'tooltip',
25229            role : 'tooltip',
25230            cn : [
25231                 {
25232                     cls : 'tooltip-arrow'
25233                 },
25234                 {
25235                     cls : 'tooltip-inner'
25236                 }
25237            ]
25238         };
25239         
25240         return cfg;
25241     },
25242     bind : function(el)
25243     {
25244         this.bindEl = el;
25245     },
25246       
25247     
25248     enter : function () {
25249        
25250         if (this.timeout != null) {
25251             clearTimeout(this.timeout);
25252         }
25253         
25254         this.hoverState = 'in';
25255          //Roo.log("enter - show");
25256         if (!this.delay || !this.delay.show) {
25257             this.show();
25258             return;
25259         }
25260         var _t = this;
25261         this.timeout = setTimeout(function () {
25262             if (_t.hoverState == 'in') {
25263                 _t.show();
25264             }
25265         }, this.delay.show);
25266     },
25267     leave : function()
25268     {
25269         clearTimeout(this.timeout);
25270     
25271         this.hoverState = 'out';
25272          if (!this.delay || !this.delay.hide) {
25273             this.hide();
25274             return;
25275         }
25276        
25277         var _t = this;
25278         this.timeout = setTimeout(function () {
25279             //Roo.log("leave - timeout");
25280             
25281             if (_t.hoverState == 'out') {
25282                 _t.hide();
25283                 Roo.bootstrap.Tooltip.currentEl = false;
25284             }
25285         }, delay);
25286     },
25287     
25288     show : function (msg)
25289     {
25290         if (!this.el) {
25291             this.render(document.body);
25292         }
25293         // set content.
25294         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25295         
25296         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25297         
25298         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25299         
25300         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25301         
25302         var placement = typeof this.placement == 'function' ?
25303             this.placement.call(this, this.el, on_el) :
25304             this.placement;
25305             
25306         var autoToken = /\s?auto?\s?/i;
25307         var autoPlace = autoToken.test(placement);
25308         if (autoPlace) {
25309             placement = placement.replace(autoToken, '') || 'top';
25310         }
25311         
25312         //this.el.detach()
25313         //this.el.setXY([0,0]);
25314         this.el.show();
25315         //this.el.dom.style.display='block';
25316         
25317         //this.el.appendTo(on_el);
25318         
25319         var p = this.getPosition();
25320         var box = this.el.getBox();
25321         
25322         if (autoPlace) {
25323             // fixme..
25324         }
25325         
25326         var align = this.alignment[placement];
25327         
25328         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25329         
25330         if(placement == 'top' || placement == 'bottom'){
25331             if(xy[0] < 0){
25332                 placement = 'right';
25333             }
25334             
25335             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25336                 placement = 'left';
25337             }
25338             
25339             var scroll = Roo.select('body', true).first().getScroll();
25340             
25341             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25342                 placement = 'top';
25343             }
25344             
25345         }
25346         
25347         this.el.alignTo(this.bindEl, align[0],align[1]);
25348         //var arrow = this.el.select('.arrow',true).first();
25349         //arrow.set(align[2], 
25350         
25351         this.el.addClass(placement);
25352         
25353         this.el.addClass('in fade');
25354         
25355         this.hoverState = null;
25356         
25357         if (this.el.hasClass('fade')) {
25358             // fade it?
25359         }
25360         
25361     },
25362     hide : function()
25363     {
25364          
25365         if (!this.el) {
25366             return;
25367         }
25368         //this.el.setXY([0,0]);
25369         this.el.removeClass('in');
25370         //this.el.hide();
25371         
25372     }
25373     
25374 });
25375  
25376
25377  /*
25378  * - LGPL
25379  *
25380  * Location Picker
25381  * 
25382  */
25383
25384 /**
25385  * @class Roo.bootstrap.LocationPicker
25386  * @extends Roo.bootstrap.Component
25387  * Bootstrap LocationPicker class
25388  * @cfg {Number} latitude Position when init default 0
25389  * @cfg {Number} longitude Position when init default 0
25390  * @cfg {Number} zoom default 15
25391  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25392  * @cfg {Boolean} mapTypeControl default false
25393  * @cfg {Boolean} disableDoubleClickZoom default false
25394  * @cfg {Boolean} scrollwheel default true
25395  * @cfg {Boolean} streetViewControl default false
25396  * @cfg {Number} radius default 0
25397  * @cfg {String} locationName
25398  * @cfg {Boolean} draggable default true
25399  * @cfg {Boolean} enableAutocomplete default false
25400  * @cfg {Boolean} enableReverseGeocode default true
25401  * @cfg {String} markerTitle
25402  * 
25403  * @constructor
25404  * Create a new LocationPicker
25405  * @param {Object} config The config object
25406  */
25407
25408
25409 Roo.bootstrap.LocationPicker = function(config){
25410     
25411     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25412     
25413     this.addEvents({
25414         /**
25415          * @event initial
25416          * Fires when the picker initialized.
25417          * @param {Roo.bootstrap.LocationPicker} this
25418          * @param {Google Location} location
25419          */
25420         initial : true,
25421         /**
25422          * @event positionchanged
25423          * Fires when the picker position changed.
25424          * @param {Roo.bootstrap.LocationPicker} this
25425          * @param {Google Location} location
25426          */
25427         positionchanged : true,
25428         /**
25429          * @event resize
25430          * Fires when the map resize.
25431          * @param {Roo.bootstrap.LocationPicker} this
25432          */
25433         resize : true,
25434         /**
25435          * @event show
25436          * Fires when the map show.
25437          * @param {Roo.bootstrap.LocationPicker} this
25438          */
25439         show : true,
25440         /**
25441          * @event hide
25442          * Fires when the map hide.
25443          * @param {Roo.bootstrap.LocationPicker} this
25444          */
25445         hide : true,
25446         /**
25447          * @event mapClick
25448          * Fires when click the map.
25449          * @param {Roo.bootstrap.LocationPicker} this
25450          * @param {Map event} e
25451          */
25452         mapClick : true,
25453         /**
25454          * @event mapRightClick
25455          * Fires when right click the map.
25456          * @param {Roo.bootstrap.LocationPicker} this
25457          * @param {Map event} e
25458          */
25459         mapRightClick : true,
25460         /**
25461          * @event markerClick
25462          * Fires when click the marker.
25463          * @param {Roo.bootstrap.LocationPicker} this
25464          * @param {Map event} e
25465          */
25466         markerClick : true,
25467         /**
25468          * @event markerRightClick
25469          * Fires when right click the marker.
25470          * @param {Roo.bootstrap.LocationPicker} this
25471          * @param {Map event} e
25472          */
25473         markerRightClick : true,
25474         /**
25475          * @event OverlayViewDraw
25476          * Fires when OverlayView Draw
25477          * @param {Roo.bootstrap.LocationPicker} this
25478          */
25479         OverlayViewDraw : true,
25480         /**
25481          * @event OverlayViewOnAdd
25482          * Fires when OverlayView Draw
25483          * @param {Roo.bootstrap.LocationPicker} this
25484          */
25485         OverlayViewOnAdd : true,
25486         /**
25487          * @event OverlayViewOnRemove
25488          * Fires when OverlayView Draw
25489          * @param {Roo.bootstrap.LocationPicker} this
25490          */
25491         OverlayViewOnRemove : true,
25492         /**
25493          * @event OverlayViewShow
25494          * Fires when OverlayView Draw
25495          * @param {Roo.bootstrap.LocationPicker} this
25496          * @param {Pixel} cpx
25497          */
25498         OverlayViewShow : true,
25499         /**
25500          * @event OverlayViewHide
25501          * Fires when OverlayView Draw
25502          * @param {Roo.bootstrap.LocationPicker} this
25503          */
25504         OverlayViewHide : true,
25505         /**
25506          * @event loadexception
25507          * Fires when load google lib failed.
25508          * @param {Roo.bootstrap.LocationPicker} this
25509          */
25510         loadexception : true
25511     });
25512         
25513 };
25514
25515 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
25516     
25517     gMapContext: false,
25518     
25519     latitude: 0,
25520     longitude: 0,
25521     zoom: 15,
25522     mapTypeId: false,
25523     mapTypeControl: false,
25524     disableDoubleClickZoom: false,
25525     scrollwheel: true,
25526     streetViewControl: false,
25527     radius: 0,
25528     locationName: '',
25529     draggable: true,
25530     enableAutocomplete: false,
25531     enableReverseGeocode: true,
25532     markerTitle: '',
25533     
25534     getAutoCreate: function()
25535     {
25536
25537         var cfg = {
25538             tag: 'div',
25539             cls: 'roo-location-picker'
25540         };
25541         
25542         return cfg
25543     },
25544     
25545     initEvents: function(ct, position)
25546     {       
25547         if(!this.el.getWidth() || this.isApplied()){
25548             return;
25549         }
25550         
25551         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25552         
25553         this.initial();
25554     },
25555     
25556     initial: function()
25557     {
25558         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
25559             this.fireEvent('loadexception', this);
25560             return;
25561         }
25562         
25563         if(!this.mapTypeId){
25564             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
25565         }
25566         
25567         this.gMapContext = this.GMapContext();
25568         
25569         this.initOverlayView();
25570         
25571         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
25572         
25573         var _this = this;
25574                 
25575         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
25576             _this.setPosition(_this.gMapContext.marker.position);
25577         });
25578         
25579         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
25580             _this.fireEvent('mapClick', this, event);
25581             
25582         });
25583
25584         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
25585             _this.fireEvent('mapRightClick', this, event);
25586             
25587         });
25588         
25589         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
25590             _this.fireEvent('markerClick', this, event);
25591             
25592         });
25593
25594         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
25595             _this.fireEvent('markerRightClick', this, event);
25596             
25597         });
25598         
25599         this.setPosition(this.gMapContext.location);
25600         
25601         this.fireEvent('initial', this, this.gMapContext.location);
25602     },
25603     
25604     initOverlayView: function()
25605     {
25606         var _this = this;
25607         
25608         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
25609             
25610             draw: function()
25611             {
25612                 _this.fireEvent('OverlayViewDraw', _this);
25613             },
25614             
25615             onAdd: function()
25616             {
25617                 _this.fireEvent('OverlayViewOnAdd', _this);
25618             },
25619             
25620             onRemove: function()
25621             {
25622                 _this.fireEvent('OverlayViewOnRemove', _this);
25623             },
25624             
25625             show: function(cpx)
25626             {
25627                 _this.fireEvent('OverlayViewShow', _this, cpx);
25628             },
25629             
25630             hide: function()
25631             {
25632                 _this.fireEvent('OverlayViewHide', _this);
25633             }
25634             
25635         });
25636     },
25637     
25638     fromLatLngToContainerPixel: function(event)
25639     {
25640         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
25641     },
25642     
25643     isApplied: function() 
25644     {
25645         return this.getGmapContext() == false ? false : true;
25646     },
25647     
25648     getGmapContext: function() 
25649     {
25650         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
25651     },
25652     
25653     GMapContext: function() 
25654     {
25655         var position = new google.maps.LatLng(this.latitude, this.longitude);
25656         
25657         var _map = new google.maps.Map(this.el.dom, {
25658             center: position,
25659             zoom: this.zoom,
25660             mapTypeId: this.mapTypeId,
25661             mapTypeControl: this.mapTypeControl,
25662             disableDoubleClickZoom: this.disableDoubleClickZoom,
25663             scrollwheel: this.scrollwheel,
25664             streetViewControl: this.streetViewControl,
25665             locationName: this.locationName,
25666             draggable: this.draggable,
25667             enableAutocomplete: this.enableAutocomplete,
25668             enableReverseGeocode: this.enableReverseGeocode
25669         });
25670         
25671         var _marker = new google.maps.Marker({
25672             position: position,
25673             map: _map,
25674             title: this.markerTitle,
25675             draggable: this.draggable
25676         });
25677         
25678         return {
25679             map: _map,
25680             marker: _marker,
25681             circle: null,
25682             location: position,
25683             radius: this.radius,
25684             locationName: this.locationName,
25685             addressComponents: {
25686                 formatted_address: null,
25687                 addressLine1: null,
25688                 addressLine2: null,
25689                 streetName: null,
25690                 streetNumber: null,
25691                 city: null,
25692                 district: null,
25693                 state: null,
25694                 stateOrProvince: null
25695             },
25696             settings: this,
25697             domContainer: this.el.dom,
25698             geodecoder: new google.maps.Geocoder()
25699         };
25700     },
25701     
25702     drawCircle: function(center, radius, options) 
25703     {
25704         if (this.gMapContext.circle != null) {
25705             this.gMapContext.circle.setMap(null);
25706         }
25707         if (radius > 0) {
25708             radius *= 1;
25709             options = Roo.apply({}, options, {
25710                 strokeColor: "#0000FF",
25711                 strokeOpacity: .35,
25712                 strokeWeight: 2,
25713                 fillColor: "#0000FF",
25714                 fillOpacity: .2
25715             });
25716             
25717             options.map = this.gMapContext.map;
25718             options.radius = radius;
25719             options.center = center;
25720             this.gMapContext.circle = new google.maps.Circle(options);
25721             return this.gMapContext.circle;
25722         }
25723         
25724         return null;
25725     },
25726     
25727     setPosition: function(location) 
25728     {
25729         this.gMapContext.location = location;
25730         this.gMapContext.marker.setPosition(location);
25731         this.gMapContext.map.panTo(location);
25732         this.drawCircle(location, this.gMapContext.radius, {});
25733         
25734         var _this = this;
25735         
25736         if (this.gMapContext.settings.enableReverseGeocode) {
25737             this.gMapContext.geodecoder.geocode({
25738                 latLng: this.gMapContext.location
25739             }, function(results, status) {
25740                 
25741                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25742                     _this.gMapContext.locationName = results[0].formatted_address;
25743                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25744                     
25745                     _this.fireEvent('positionchanged', this, location);
25746                 }
25747             });
25748             
25749             return;
25750         }
25751         
25752         this.fireEvent('positionchanged', this, location);
25753     },
25754     
25755     resize: function()
25756     {
25757         google.maps.event.trigger(this.gMapContext.map, "resize");
25758         
25759         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25760         
25761         this.fireEvent('resize', this);
25762     },
25763     
25764     setPositionByLatLng: function(latitude, longitude)
25765     {
25766         this.setPosition(new google.maps.LatLng(latitude, longitude));
25767     },
25768     
25769     getCurrentPosition: function() 
25770     {
25771         return {
25772             latitude: this.gMapContext.location.lat(),
25773             longitude: this.gMapContext.location.lng()
25774         };
25775     },
25776     
25777     getAddressName: function() 
25778     {
25779         return this.gMapContext.locationName;
25780     },
25781     
25782     getAddressComponents: function() 
25783     {
25784         return this.gMapContext.addressComponents;
25785     },
25786     
25787     address_component_from_google_geocode: function(address_components) 
25788     {
25789         var result = {};
25790         
25791         for (var i = 0; i < address_components.length; i++) {
25792             var component = address_components[i];
25793             if (component.types.indexOf("postal_code") >= 0) {
25794                 result.postalCode = component.short_name;
25795             } else if (component.types.indexOf("street_number") >= 0) {
25796                 result.streetNumber = component.short_name;
25797             } else if (component.types.indexOf("route") >= 0) {
25798                 result.streetName = component.short_name;
25799             } else if (component.types.indexOf("neighborhood") >= 0) {
25800                 result.city = component.short_name;
25801             } else if (component.types.indexOf("locality") >= 0) {
25802                 result.city = component.short_name;
25803             } else if (component.types.indexOf("sublocality") >= 0) {
25804                 result.district = component.short_name;
25805             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25806                 result.stateOrProvince = component.short_name;
25807             } else if (component.types.indexOf("country") >= 0) {
25808                 result.country = component.short_name;
25809             }
25810         }
25811         
25812         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25813         result.addressLine2 = "";
25814         return result;
25815     },
25816     
25817     setZoomLevel: function(zoom)
25818     {
25819         this.gMapContext.map.setZoom(zoom);
25820     },
25821     
25822     show: function()
25823     {
25824         if(!this.el){
25825             return;
25826         }
25827         
25828         this.el.show();
25829         
25830         this.resize();
25831         
25832         this.fireEvent('show', this);
25833     },
25834     
25835     hide: function()
25836     {
25837         if(!this.el){
25838             return;
25839         }
25840         
25841         this.el.hide();
25842         
25843         this.fireEvent('hide', this);
25844     }
25845     
25846 });
25847
25848 Roo.apply(Roo.bootstrap.LocationPicker, {
25849     
25850     OverlayView : function(map, options)
25851     {
25852         options = options || {};
25853         
25854         this.setMap(map);
25855     }
25856     
25857     
25858 });/*
25859  * - LGPL
25860  *
25861  * Alert
25862  * 
25863  */
25864
25865 /**
25866  * @class Roo.bootstrap.Alert
25867  * @extends Roo.bootstrap.Component
25868  * Bootstrap Alert class
25869  * @cfg {String} title The title of alert
25870  * @cfg {String} html The content of alert
25871  * @cfg {String} weight (  success | info | warning | danger )
25872  * @cfg {String} faicon font-awesomeicon
25873  * 
25874  * @constructor
25875  * Create a new alert
25876  * @param {Object} config The config object
25877  */
25878
25879
25880 Roo.bootstrap.Alert = function(config){
25881     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25882     
25883 };
25884
25885 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
25886     
25887     title: '',
25888     html: '',
25889     weight: false,
25890     faicon: false,
25891     
25892     getAutoCreate : function()
25893     {
25894         
25895         var cfg = {
25896             tag : 'div',
25897             cls : 'alert',
25898             cn : [
25899                 {
25900                     tag : 'i',
25901                     cls : 'roo-alert-icon'
25902                     
25903                 },
25904                 {
25905                     tag : 'b',
25906                     cls : 'roo-alert-title',
25907                     html : this.title
25908                 },
25909                 {
25910                     tag : 'span',
25911                     cls : 'roo-alert-text',
25912                     html : this.html
25913                 }
25914             ]
25915         };
25916         
25917         if(this.faicon){
25918             cfg.cn[0].cls += ' fa ' + this.faicon;
25919         }
25920         
25921         if(this.weight){
25922             cfg.cls += ' alert-' + this.weight;
25923         }
25924         
25925         return cfg;
25926     },
25927     
25928     initEvents: function() 
25929     {
25930         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25931     },
25932     
25933     setTitle : function(str)
25934     {
25935         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25936     },
25937     
25938     setText : function(str)
25939     {
25940         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25941     },
25942     
25943     setWeight : function(weight)
25944     {
25945         if(this.weight){
25946             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25947         }
25948         
25949         this.weight = weight;
25950         
25951         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25952     },
25953     
25954     setIcon : function(icon)
25955     {
25956         if(this.faicon){
25957             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25958         }
25959         
25960         this.faicon = icon;
25961         
25962         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25963     },
25964     
25965     hide: function() 
25966     {
25967         this.el.hide();   
25968     },
25969     
25970     show: function() 
25971     {  
25972         this.el.show();   
25973     }
25974     
25975 });
25976
25977  
25978 /*
25979 * Licence: LGPL
25980 */
25981
25982 /**
25983  * @class Roo.bootstrap.UploadCropbox
25984  * @extends Roo.bootstrap.Component
25985  * Bootstrap UploadCropbox class
25986  * @cfg {String} emptyText show when image has been loaded
25987  * @cfg {String} rotateNotify show when image too small to rotate
25988  * @cfg {Number} errorTimeout default 3000
25989  * @cfg {Number} minWidth default 300
25990  * @cfg {Number} minHeight default 300
25991  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25992  * @cfg {Boolean} isDocument (true|false) default false
25993  * @cfg {String} url action url
25994  * @cfg {String} paramName default 'imageUpload'
25995  * @cfg {String} method default POST
25996  * @cfg {Boolean} loadMask (true|false) default true
25997  * @cfg {Boolean} loadingText default 'Loading...'
25998  * 
25999  * @constructor
26000  * Create a new UploadCropbox
26001  * @param {Object} config The config object
26002  */
26003
26004 Roo.bootstrap.UploadCropbox = function(config){
26005     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26006     
26007     this.addEvents({
26008         /**
26009          * @event beforeselectfile
26010          * Fire before select file
26011          * @param {Roo.bootstrap.UploadCropbox} this
26012          */
26013         "beforeselectfile" : true,
26014         /**
26015          * @event initial
26016          * Fire after initEvent
26017          * @param {Roo.bootstrap.UploadCropbox} this
26018          */
26019         "initial" : true,
26020         /**
26021          * @event crop
26022          * Fire after initEvent
26023          * @param {Roo.bootstrap.UploadCropbox} this
26024          * @param {String} data
26025          */
26026         "crop" : true,
26027         /**
26028          * @event prepare
26029          * Fire when preparing the file data
26030          * @param {Roo.bootstrap.UploadCropbox} this
26031          * @param {Object} file
26032          */
26033         "prepare" : true,
26034         /**
26035          * @event exception
26036          * Fire when get exception
26037          * @param {Roo.bootstrap.UploadCropbox} this
26038          * @param {XMLHttpRequest} xhr
26039          */
26040         "exception" : true,
26041         /**
26042          * @event beforeloadcanvas
26043          * Fire before load the canvas
26044          * @param {Roo.bootstrap.UploadCropbox} this
26045          * @param {String} src
26046          */
26047         "beforeloadcanvas" : true,
26048         /**
26049          * @event trash
26050          * Fire when trash image
26051          * @param {Roo.bootstrap.UploadCropbox} this
26052          */
26053         "trash" : true,
26054         /**
26055          * @event download
26056          * Fire when download the image
26057          * @param {Roo.bootstrap.UploadCropbox} this
26058          */
26059         "download" : true,
26060         /**
26061          * @event footerbuttonclick
26062          * Fire when footerbuttonclick
26063          * @param {Roo.bootstrap.UploadCropbox} this
26064          * @param {String} type
26065          */
26066         "footerbuttonclick" : true,
26067         /**
26068          * @event resize
26069          * Fire when resize
26070          * @param {Roo.bootstrap.UploadCropbox} this
26071          */
26072         "resize" : true,
26073         /**
26074          * @event rotate
26075          * Fire when rotate the image
26076          * @param {Roo.bootstrap.UploadCropbox} this
26077          * @param {String} pos
26078          */
26079         "rotate" : true,
26080         /**
26081          * @event inspect
26082          * Fire when inspect the file
26083          * @param {Roo.bootstrap.UploadCropbox} this
26084          * @param {Object} file
26085          */
26086         "inspect" : true,
26087         /**
26088          * @event upload
26089          * Fire when xhr upload the file
26090          * @param {Roo.bootstrap.UploadCropbox} this
26091          * @param {Object} data
26092          */
26093         "upload" : true,
26094         /**
26095          * @event arrange
26096          * Fire when arrange the file data
26097          * @param {Roo.bootstrap.UploadCropbox} this
26098          * @param {Object} formData
26099          */
26100         "arrange" : true
26101     });
26102     
26103     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26104 };
26105
26106 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26107     
26108     emptyText : 'Click to upload image',
26109     rotateNotify : 'Image is too small to rotate',
26110     errorTimeout : 3000,
26111     scale : 0,
26112     baseScale : 1,
26113     rotate : 0,
26114     dragable : false,
26115     pinching : false,
26116     mouseX : 0,
26117     mouseY : 0,
26118     cropData : false,
26119     minWidth : 300,
26120     minHeight : 300,
26121     file : false,
26122     exif : {},
26123     baseRotate : 1,
26124     cropType : 'image/jpeg',
26125     buttons : false,
26126     canvasLoaded : false,
26127     isDocument : false,
26128     method : 'POST',
26129     paramName : 'imageUpload',
26130     loadMask : true,
26131     loadingText : 'Loading...',
26132     maskEl : false,
26133     
26134     getAutoCreate : function()
26135     {
26136         var cfg = {
26137             tag : 'div',
26138             cls : 'roo-upload-cropbox',
26139             cn : [
26140                 {
26141                     tag : 'input',
26142                     cls : 'roo-upload-cropbox-selector',
26143                     type : 'file'
26144                 },
26145                 {
26146                     tag : 'div',
26147                     cls : 'roo-upload-cropbox-body',
26148                     style : 'cursor:pointer',
26149                     cn : [
26150                         {
26151                             tag : 'div',
26152                             cls : 'roo-upload-cropbox-preview'
26153                         },
26154                         {
26155                             tag : 'div',
26156                             cls : 'roo-upload-cropbox-thumb'
26157                         },
26158                         {
26159                             tag : 'div',
26160                             cls : 'roo-upload-cropbox-empty-notify',
26161                             html : this.emptyText
26162                         },
26163                         {
26164                             tag : 'div',
26165                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26166                             html : this.rotateNotify
26167                         }
26168                     ]
26169                 },
26170                 {
26171                     tag : 'div',
26172                     cls : 'roo-upload-cropbox-footer',
26173                     cn : {
26174                         tag : 'div',
26175                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26176                         cn : []
26177                     }
26178                 }
26179             ]
26180         };
26181         
26182         return cfg;
26183     },
26184     
26185     onRender : function(ct, position)
26186     {
26187         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26188         
26189         if (this.buttons.length) {
26190             
26191             Roo.each(this.buttons, function(bb) {
26192                 
26193                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26194                 
26195                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26196                 
26197             }, this);
26198         }
26199         
26200         if(this.loadMask){
26201             this.maskEl = this.el;
26202         }
26203     },
26204     
26205     initEvents : function()
26206     {
26207         this.urlAPI = (window.createObjectURL && window) || 
26208                                 (window.URL && URL.revokeObjectURL && URL) || 
26209                                 (window.webkitURL && webkitURL);
26210                         
26211         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26212         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26213         
26214         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26215         this.selectorEl.hide();
26216         
26217         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26218         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26219         
26220         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26221         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26222         this.thumbEl.hide();
26223         
26224         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26225         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26226         
26227         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26228         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26229         this.errorEl.hide();
26230         
26231         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26232         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26233         this.footerEl.hide();
26234         
26235         this.setThumbBoxSize();
26236         
26237         this.bind();
26238         
26239         this.resize();
26240         
26241         this.fireEvent('initial', this);
26242     },
26243
26244     bind : function()
26245     {
26246         var _this = this;
26247         
26248         window.addEventListener("resize", function() { _this.resize(); } );
26249         
26250         this.bodyEl.on('click', this.beforeSelectFile, this);
26251         
26252         if(Roo.isTouch){
26253             this.bodyEl.on('touchstart', this.onTouchStart, this);
26254             this.bodyEl.on('touchmove', this.onTouchMove, this);
26255             this.bodyEl.on('touchend', this.onTouchEnd, this);
26256         }
26257         
26258         if(!Roo.isTouch){
26259             this.bodyEl.on('mousedown', this.onMouseDown, this);
26260             this.bodyEl.on('mousemove', this.onMouseMove, this);
26261             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26262             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26263             Roo.get(document).on('mouseup', this.onMouseUp, this);
26264         }
26265         
26266         this.selectorEl.on('change', this.onFileSelected, this);
26267     },
26268     
26269     reset : function()
26270     {    
26271         this.scale = 0;
26272         this.baseScale = 1;
26273         this.rotate = 0;
26274         this.baseRotate = 1;
26275         this.dragable = false;
26276         this.pinching = false;
26277         this.mouseX = 0;
26278         this.mouseY = 0;
26279         this.cropData = false;
26280         this.notifyEl.dom.innerHTML = this.emptyText;
26281         
26282         this.selectorEl.dom.value = '';
26283         
26284     },
26285     
26286     resize : function()
26287     {
26288         if(this.fireEvent('resize', this) != false){
26289             this.setThumbBoxPosition();
26290             this.setCanvasPosition();
26291         }
26292     },
26293     
26294     onFooterButtonClick : function(e, el, o, type)
26295     {
26296         switch (type) {
26297             case 'rotate-left' :
26298                 this.onRotateLeft(e);
26299                 break;
26300             case 'rotate-right' :
26301                 this.onRotateRight(e);
26302                 break;
26303             case 'picture' :
26304                 this.beforeSelectFile(e);
26305                 break;
26306             case 'trash' :
26307                 this.trash(e);
26308                 break;
26309             case 'crop' :
26310                 this.crop(e);
26311                 break;
26312             case 'download' :
26313                 this.download(e);
26314                 break;
26315             default :
26316                 break;
26317         }
26318         
26319         this.fireEvent('footerbuttonclick', this, type);
26320     },
26321     
26322     beforeSelectFile : function(e)
26323     {
26324         e.preventDefault();
26325         
26326         if(this.fireEvent('beforeselectfile', this) != false){
26327             this.selectorEl.dom.click();
26328         }
26329     },
26330     
26331     onFileSelected : function(e)
26332     {
26333         e.preventDefault();
26334         
26335         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26336             return;
26337         }
26338         
26339         var file = this.selectorEl.dom.files[0];
26340         
26341         if(this.fireEvent('inspect', this, file) != false){
26342             this.prepare(file);
26343         }
26344         
26345     },
26346     
26347     trash : function(e)
26348     {
26349         this.fireEvent('trash', this);
26350     },
26351     
26352     download : function(e)
26353     {
26354         this.fireEvent('download', this);
26355     },
26356     
26357     loadCanvas : function(src)
26358     {   
26359         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26360             
26361             this.reset();
26362             
26363             this.imageEl = document.createElement('img');
26364             
26365             var _this = this;
26366             
26367             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26368             
26369             this.imageEl.src = src;
26370         }
26371     },
26372     
26373     onLoadCanvas : function()
26374     {   
26375         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26376         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26377         
26378         this.bodyEl.un('click', this.beforeSelectFile, this);
26379         
26380         this.notifyEl.hide();
26381         this.thumbEl.show();
26382         this.footerEl.show();
26383         
26384         this.baseRotateLevel();
26385         
26386         if(this.isDocument){
26387             this.setThumbBoxSize();
26388         }
26389         
26390         this.setThumbBoxPosition();
26391         
26392         this.baseScaleLevel();
26393         
26394         this.draw();
26395         
26396         this.resize();
26397         
26398         this.canvasLoaded = true;
26399         
26400         if(this.loadMask){
26401             this.maskEl.unmask();
26402         }
26403         
26404     },
26405     
26406     setCanvasPosition : function()
26407     {   
26408         if(!this.canvasEl){
26409             return;
26410         }
26411         
26412         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26413         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26414         
26415         this.previewEl.setLeft(pw);
26416         this.previewEl.setTop(ph);
26417         
26418     },
26419     
26420     onMouseDown : function(e)
26421     {   
26422         e.stopEvent();
26423         
26424         this.dragable = true;
26425         this.pinching = false;
26426         
26427         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26428             this.dragable = false;
26429             return;
26430         }
26431         
26432         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26433         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26434         
26435     },
26436     
26437     onMouseMove : function(e)
26438     {   
26439         e.stopEvent();
26440         
26441         if(!this.canvasLoaded){
26442             return;
26443         }
26444         
26445         if (!this.dragable){
26446             return;
26447         }
26448         
26449         var minX = Math.ceil(this.thumbEl.getLeft(true));
26450         var minY = Math.ceil(this.thumbEl.getTop(true));
26451         
26452         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26453         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26454         
26455         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26456         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26457         
26458         x = x - this.mouseX;
26459         y = y - this.mouseY;
26460         
26461         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26462         var bgY = Math.ceil(y + this.previewEl.getTop(true));
26463         
26464         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26465         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26466         
26467         this.previewEl.setLeft(bgX);
26468         this.previewEl.setTop(bgY);
26469         
26470         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26471         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26472     },
26473     
26474     onMouseUp : function(e)
26475     {   
26476         e.stopEvent();
26477         
26478         this.dragable = false;
26479     },
26480     
26481     onMouseWheel : function(e)
26482     {   
26483         e.stopEvent();
26484         
26485         this.startScale = this.scale;
26486         
26487         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26488         
26489         if(!this.zoomable()){
26490             this.scale = this.startScale;
26491             return;
26492         }
26493         
26494         this.draw();
26495         
26496         return;
26497     },
26498     
26499     zoomable : function()
26500     {
26501         var minScale = this.thumbEl.getWidth() / this.minWidth;
26502         
26503         if(this.minWidth < this.minHeight){
26504             minScale = this.thumbEl.getHeight() / this.minHeight;
26505         }
26506         
26507         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26508         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26509         
26510         if(
26511                 this.isDocument &&
26512                 (this.rotate == 0 || this.rotate == 180) && 
26513                 (
26514                     width > this.imageEl.OriginWidth || 
26515                     height > this.imageEl.OriginHeight ||
26516                     (width < this.minWidth && height < this.minHeight)
26517                 )
26518         ){
26519             return false;
26520         }
26521         
26522         if(
26523                 this.isDocument &&
26524                 (this.rotate == 90 || this.rotate == 270) && 
26525                 (
26526                     width > this.imageEl.OriginWidth || 
26527                     height > this.imageEl.OriginHeight ||
26528                     (width < this.minHeight && height < this.minWidth)
26529                 )
26530         ){
26531             return false;
26532         }
26533         
26534         if(
26535                 !this.isDocument &&
26536                 (this.rotate == 0 || this.rotate == 180) && 
26537                 (
26538                     width < this.minWidth || 
26539                     width > this.imageEl.OriginWidth || 
26540                     height < this.minHeight || 
26541                     height > this.imageEl.OriginHeight
26542                 )
26543         ){
26544             return false;
26545         }
26546         
26547         if(
26548                 !this.isDocument &&
26549                 (this.rotate == 90 || this.rotate == 270) && 
26550                 (
26551                     width < this.minHeight || 
26552                     width > this.imageEl.OriginWidth || 
26553                     height < this.minWidth || 
26554                     height > this.imageEl.OriginHeight
26555                 )
26556         ){
26557             return false;
26558         }
26559         
26560         return true;
26561         
26562     },
26563     
26564     onRotateLeft : function(e)
26565     {   
26566         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26567             
26568             var minScale = this.thumbEl.getWidth() / this.minWidth;
26569             
26570             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26571             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26572             
26573             this.startScale = this.scale;
26574             
26575             while (this.getScaleLevel() < minScale){
26576             
26577                 this.scale = this.scale + 1;
26578                 
26579                 if(!this.zoomable()){
26580                     break;
26581                 }
26582                 
26583                 if(
26584                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26585                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26586                 ){
26587                     continue;
26588                 }
26589                 
26590                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26591
26592                 this.draw();
26593                 
26594                 return;
26595             }
26596             
26597             this.scale = this.startScale;
26598             
26599             this.onRotateFail();
26600             
26601             return false;
26602         }
26603         
26604         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26605
26606         if(this.isDocument){
26607             this.setThumbBoxSize();
26608             this.setThumbBoxPosition();
26609             this.setCanvasPosition();
26610         }
26611         
26612         this.draw();
26613         
26614         this.fireEvent('rotate', this, 'left');
26615         
26616     },
26617     
26618     onRotateRight : function(e)
26619     {
26620         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26621             
26622             var minScale = this.thumbEl.getWidth() / this.minWidth;
26623         
26624             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26625             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26626             
26627             this.startScale = this.scale;
26628             
26629             while (this.getScaleLevel() < minScale){
26630             
26631                 this.scale = this.scale + 1;
26632                 
26633                 if(!this.zoomable()){
26634                     break;
26635                 }
26636                 
26637                 if(
26638                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26639                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26640                 ){
26641                     continue;
26642                 }
26643                 
26644                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26645
26646                 this.draw();
26647                 
26648                 return;
26649             }
26650             
26651             this.scale = this.startScale;
26652             
26653             this.onRotateFail();
26654             
26655             return false;
26656         }
26657         
26658         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26659
26660         if(this.isDocument){
26661             this.setThumbBoxSize();
26662             this.setThumbBoxPosition();
26663             this.setCanvasPosition();
26664         }
26665         
26666         this.draw();
26667         
26668         this.fireEvent('rotate', this, 'right');
26669     },
26670     
26671     onRotateFail : function()
26672     {
26673         this.errorEl.show(true);
26674         
26675         var _this = this;
26676         
26677         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26678     },
26679     
26680     draw : function()
26681     {
26682         this.previewEl.dom.innerHTML = '';
26683         
26684         var canvasEl = document.createElement("canvas");
26685         
26686         var contextEl = canvasEl.getContext("2d");
26687         
26688         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26689         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26690         var center = this.imageEl.OriginWidth / 2;
26691         
26692         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26693             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26694             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26695             center = this.imageEl.OriginHeight / 2;
26696         }
26697         
26698         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26699         
26700         contextEl.translate(center, center);
26701         contextEl.rotate(this.rotate * Math.PI / 180);
26702
26703         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26704         
26705         this.canvasEl = document.createElement("canvas");
26706         
26707         this.contextEl = this.canvasEl.getContext("2d");
26708         
26709         switch (this.rotate) {
26710             case 0 :
26711                 
26712                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26713                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
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 90 : 
26719                 
26720                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26721                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26722                 
26723                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26724                     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);
26725                     break;
26726                 }
26727                 
26728                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26729                 
26730                 break;
26731             case 180 :
26732                 
26733                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26734                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26735                 
26736                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26737                     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);
26738                     break;
26739                 }
26740                 
26741                 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);
26742                 
26743                 break;
26744             case 270 :
26745                 
26746                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26747                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26748         
26749                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26750                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26751                     break;
26752                 }
26753                 
26754                 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);
26755                 
26756                 break;
26757             default : 
26758                 break;
26759         }
26760         
26761         this.previewEl.appendChild(this.canvasEl);
26762         
26763         this.setCanvasPosition();
26764     },
26765     
26766     crop : function()
26767     {
26768         if(!this.canvasLoaded){
26769             return;
26770         }
26771         
26772         var imageCanvas = document.createElement("canvas");
26773         
26774         var imageContext = imageCanvas.getContext("2d");
26775         
26776         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26777         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26778         
26779         var center = imageCanvas.width / 2;
26780         
26781         imageContext.translate(center, center);
26782         
26783         imageContext.rotate(this.rotate * Math.PI / 180);
26784         
26785         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26786         
26787         var canvas = document.createElement("canvas");
26788         
26789         var context = canvas.getContext("2d");
26790                 
26791         canvas.width = this.minWidth;
26792         canvas.height = this.minHeight;
26793
26794         switch (this.rotate) {
26795             case 0 :
26796                 
26797                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26798                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26799                 
26800                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26801                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26802                 
26803                 var targetWidth = this.minWidth - 2 * x;
26804                 var targetHeight = this.minHeight - 2 * y;
26805                 
26806                 var scale = 1;
26807                 
26808                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26809                     scale = targetWidth / width;
26810                 }
26811                 
26812                 if(x > 0 && y == 0){
26813                     scale = targetHeight / height;
26814                 }
26815                 
26816                 if(x > 0 && y > 0){
26817                     scale = targetWidth / width;
26818                     
26819                     if(width < height){
26820                         scale = targetHeight / height;
26821                     }
26822                 }
26823                 
26824                 context.scale(scale, scale);
26825                 
26826                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26827                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26828
26829                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26830                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26831
26832                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26833                 
26834                 break;
26835             case 90 : 
26836                 
26837                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26838                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26839                 
26840                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26841                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26842                 
26843                 var targetWidth = this.minWidth - 2 * x;
26844                 var targetHeight = this.minHeight - 2 * y;
26845                 
26846                 var scale = 1;
26847                 
26848                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26849                     scale = targetWidth / width;
26850                 }
26851                 
26852                 if(x > 0 && y == 0){
26853                     scale = targetHeight / height;
26854                 }
26855                 
26856                 if(x > 0 && y > 0){
26857                     scale = targetWidth / width;
26858                     
26859                     if(width < height){
26860                         scale = targetHeight / height;
26861                     }
26862                 }
26863                 
26864                 context.scale(scale, scale);
26865                 
26866                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26867                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26868
26869                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26870                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26871                 
26872                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26873                 
26874                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26875                 
26876                 break;
26877             case 180 :
26878                 
26879                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26880                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26881                 
26882                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26883                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26884                 
26885                 var targetWidth = this.minWidth - 2 * x;
26886                 var targetHeight = this.minHeight - 2 * y;
26887                 
26888                 var scale = 1;
26889                 
26890                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26891                     scale = targetWidth / width;
26892                 }
26893                 
26894                 if(x > 0 && y == 0){
26895                     scale = targetHeight / height;
26896                 }
26897                 
26898                 if(x > 0 && y > 0){
26899                     scale = targetWidth / width;
26900                     
26901                     if(width < height){
26902                         scale = targetHeight / height;
26903                     }
26904                 }
26905                 
26906                 context.scale(scale, scale);
26907                 
26908                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26909                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26910
26911                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26912                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26913
26914                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26915                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26916                 
26917                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26918                 
26919                 break;
26920             case 270 :
26921                 
26922                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26923                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26924                 
26925                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26926                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26927                 
26928                 var targetWidth = this.minWidth - 2 * x;
26929                 var targetHeight = this.minHeight - 2 * y;
26930                 
26931                 var scale = 1;
26932                 
26933                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26934                     scale = targetWidth / width;
26935                 }
26936                 
26937                 if(x > 0 && y == 0){
26938                     scale = targetHeight / height;
26939                 }
26940                 
26941                 if(x > 0 && y > 0){
26942                     scale = targetWidth / width;
26943                     
26944                     if(width < height){
26945                         scale = targetHeight / height;
26946                     }
26947                 }
26948                 
26949                 context.scale(scale, scale);
26950                 
26951                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26952                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26953
26954                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26955                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26956                 
26957                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26958                 
26959                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26960                 
26961                 break;
26962             default : 
26963                 break;
26964         }
26965         
26966         this.cropData = canvas.toDataURL(this.cropType);
26967         
26968         if(this.fireEvent('crop', this, this.cropData) !== false){
26969             this.process(this.file, this.cropData);
26970         }
26971         
26972         return;
26973         
26974     },
26975     
26976     setThumbBoxSize : function()
26977     {
26978         var width, height;
26979         
26980         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26981             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26982             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26983             
26984             this.minWidth = width;
26985             this.minHeight = height;
26986             
26987             if(this.rotate == 90 || this.rotate == 270){
26988                 this.minWidth = height;
26989                 this.minHeight = width;
26990             }
26991         }
26992         
26993         height = 300;
26994         width = Math.ceil(this.minWidth * height / this.minHeight);
26995         
26996         if(this.minWidth > this.minHeight){
26997             width = 300;
26998             height = Math.ceil(this.minHeight * width / this.minWidth);
26999         }
27000         
27001         this.thumbEl.setStyle({
27002             width : width + 'px',
27003             height : height + 'px'
27004         });
27005
27006         return;
27007             
27008     },
27009     
27010     setThumbBoxPosition : function()
27011     {
27012         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27013         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27014         
27015         this.thumbEl.setLeft(x);
27016         this.thumbEl.setTop(y);
27017         
27018     },
27019     
27020     baseRotateLevel : function()
27021     {
27022         this.baseRotate = 1;
27023         
27024         if(
27025                 typeof(this.exif) != 'undefined' &&
27026                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27027                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27028         ){
27029             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27030         }
27031         
27032         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27033         
27034     },
27035     
27036     baseScaleLevel : function()
27037     {
27038         var width, height;
27039         
27040         if(this.isDocument){
27041             
27042             if(this.baseRotate == 6 || this.baseRotate == 8){
27043             
27044                 height = this.thumbEl.getHeight();
27045                 this.baseScale = height / this.imageEl.OriginWidth;
27046
27047                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27048                     width = this.thumbEl.getWidth();
27049                     this.baseScale = width / this.imageEl.OriginHeight;
27050                 }
27051
27052                 return;
27053             }
27054
27055             height = this.thumbEl.getHeight();
27056             this.baseScale = height / this.imageEl.OriginHeight;
27057
27058             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27059                 width = this.thumbEl.getWidth();
27060                 this.baseScale = width / this.imageEl.OriginWidth;
27061             }
27062
27063             return;
27064         }
27065         
27066         if(this.baseRotate == 6 || this.baseRotate == 8){
27067             
27068             width = this.thumbEl.getHeight();
27069             this.baseScale = width / this.imageEl.OriginHeight;
27070             
27071             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27072                 height = this.thumbEl.getWidth();
27073                 this.baseScale = height / this.imageEl.OriginHeight;
27074             }
27075             
27076             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27077                 height = this.thumbEl.getWidth();
27078                 this.baseScale = height / this.imageEl.OriginHeight;
27079                 
27080                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27081                     width = this.thumbEl.getHeight();
27082                     this.baseScale = width / this.imageEl.OriginWidth;
27083                 }
27084             }
27085             
27086             return;
27087         }
27088         
27089         width = this.thumbEl.getWidth();
27090         this.baseScale = width / this.imageEl.OriginWidth;
27091         
27092         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27093             height = this.thumbEl.getHeight();
27094             this.baseScale = height / this.imageEl.OriginHeight;
27095         }
27096         
27097         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27098             
27099             height = this.thumbEl.getHeight();
27100             this.baseScale = height / this.imageEl.OriginHeight;
27101             
27102             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27103                 width = this.thumbEl.getWidth();
27104                 this.baseScale = width / this.imageEl.OriginWidth;
27105             }
27106             
27107         }
27108         
27109         return;
27110     },
27111     
27112     getScaleLevel : function()
27113     {
27114         return this.baseScale * Math.pow(1.1, this.scale);
27115     },
27116     
27117     onTouchStart : function(e)
27118     {
27119         if(!this.canvasLoaded){
27120             this.beforeSelectFile(e);
27121             return;
27122         }
27123         
27124         var touches = e.browserEvent.touches;
27125         
27126         if(!touches){
27127             return;
27128         }
27129         
27130         if(touches.length == 1){
27131             this.onMouseDown(e);
27132             return;
27133         }
27134         
27135         if(touches.length != 2){
27136             return;
27137         }
27138         
27139         var coords = [];
27140         
27141         for(var i = 0, finger; finger = touches[i]; i++){
27142             coords.push(finger.pageX, finger.pageY);
27143         }
27144         
27145         var x = Math.pow(coords[0] - coords[2], 2);
27146         var y = Math.pow(coords[1] - coords[3], 2);
27147         
27148         this.startDistance = Math.sqrt(x + y);
27149         
27150         this.startScale = this.scale;
27151         
27152         this.pinching = true;
27153         this.dragable = false;
27154         
27155     },
27156     
27157     onTouchMove : function(e)
27158     {
27159         if(!this.pinching && !this.dragable){
27160             return;
27161         }
27162         
27163         var touches = e.browserEvent.touches;
27164         
27165         if(!touches){
27166             return;
27167         }
27168         
27169         if(this.dragable){
27170             this.onMouseMove(e);
27171             return;
27172         }
27173         
27174         var coords = [];
27175         
27176         for(var i = 0, finger; finger = touches[i]; i++){
27177             coords.push(finger.pageX, finger.pageY);
27178         }
27179         
27180         var x = Math.pow(coords[0] - coords[2], 2);
27181         var y = Math.pow(coords[1] - coords[3], 2);
27182         
27183         this.endDistance = Math.sqrt(x + y);
27184         
27185         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27186         
27187         if(!this.zoomable()){
27188             this.scale = this.startScale;
27189             return;
27190         }
27191         
27192         this.draw();
27193         
27194     },
27195     
27196     onTouchEnd : function(e)
27197     {
27198         this.pinching = false;
27199         this.dragable = false;
27200         
27201     },
27202     
27203     process : function(file, crop)
27204     {
27205         if(this.loadMask){
27206             this.maskEl.mask(this.loadingText);
27207         }
27208         
27209         this.xhr = new XMLHttpRequest();
27210         
27211         file.xhr = this.xhr;
27212
27213         this.xhr.open(this.method, this.url, true);
27214         
27215         var headers = {
27216             "Accept": "application/json",
27217             "Cache-Control": "no-cache",
27218             "X-Requested-With": "XMLHttpRequest"
27219         };
27220         
27221         for (var headerName in headers) {
27222             var headerValue = headers[headerName];
27223             if (headerValue) {
27224                 this.xhr.setRequestHeader(headerName, headerValue);
27225             }
27226         }
27227         
27228         var _this = this;
27229         
27230         this.xhr.onload = function()
27231         {
27232             _this.xhrOnLoad(_this.xhr);
27233         }
27234         
27235         this.xhr.onerror = function()
27236         {
27237             _this.xhrOnError(_this.xhr);
27238         }
27239         
27240         var formData = new FormData();
27241
27242         formData.append('returnHTML', 'NO');
27243         
27244         if(crop){
27245             formData.append('crop', crop);
27246         }
27247         
27248         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27249             formData.append(this.paramName, file, file.name);
27250         }
27251         
27252         if(typeof(file.filename) != 'undefined'){
27253             formData.append('filename', file.filename);
27254         }
27255         
27256         if(typeof(file.mimetype) != 'undefined'){
27257             formData.append('mimetype', file.mimetype);
27258         }
27259         
27260         if(this.fireEvent('arrange', this, formData) != false){
27261             this.xhr.send(formData);
27262         };
27263     },
27264     
27265     xhrOnLoad : function(xhr)
27266     {
27267         if(this.loadMask){
27268             this.maskEl.unmask();
27269         }
27270         
27271         if (xhr.readyState !== 4) {
27272             this.fireEvent('exception', this, xhr);
27273             return;
27274         }
27275
27276         var response = Roo.decode(xhr.responseText);
27277         
27278         if(!response.success){
27279             this.fireEvent('exception', this, xhr);
27280             return;
27281         }
27282         
27283         var response = Roo.decode(xhr.responseText);
27284         
27285         this.fireEvent('upload', this, response);
27286         
27287     },
27288     
27289     xhrOnError : function()
27290     {
27291         if(this.loadMask){
27292             this.maskEl.unmask();
27293         }
27294         
27295         Roo.log('xhr on error');
27296         
27297         var response = Roo.decode(xhr.responseText);
27298           
27299         Roo.log(response);
27300         
27301     },
27302     
27303     prepare : function(file)
27304     {   
27305         if(this.loadMask){
27306             this.maskEl.mask(this.loadingText);
27307         }
27308         
27309         this.file = false;
27310         this.exif = {};
27311         
27312         if(typeof(file) === 'string'){
27313             this.loadCanvas(file);
27314             return;
27315         }
27316         
27317         if(!file || !this.urlAPI){
27318             return;
27319         }
27320         
27321         this.file = file;
27322         this.cropType = file.type;
27323         
27324         var _this = this;
27325         
27326         if(this.fireEvent('prepare', this, this.file) != false){
27327             
27328             var reader = new FileReader();
27329             
27330             reader.onload = function (e) {
27331                 if (e.target.error) {
27332                     Roo.log(e.target.error);
27333                     return;
27334                 }
27335                 
27336                 var buffer = e.target.result,
27337                     dataView = new DataView(buffer),
27338                     offset = 2,
27339                     maxOffset = dataView.byteLength - 4,
27340                     markerBytes,
27341                     markerLength;
27342                 
27343                 if (dataView.getUint16(0) === 0xffd8) {
27344                     while (offset < maxOffset) {
27345                         markerBytes = dataView.getUint16(offset);
27346                         
27347                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27348                             markerLength = dataView.getUint16(offset + 2) + 2;
27349                             if (offset + markerLength > dataView.byteLength) {
27350                                 Roo.log('Invalid meta data: Invalid segment size.');
27351                                 break;
27352                             }
27353                             
27354                             if(markerBytes == 0xffe1){
27355                                 _this.parseExifData(
27356                                     dataView,
27357                                     offset,
27358                                     markerLength
27359                                 );
27360                             }
27361                             
27362                             offset += markerLength;
27363                             
27364                             continue;
27365                         }
27366                         
27367                         break;
27368                     }
27369                     
27370                 }
27371                 
27372                 var url = _this.urlAPI.createObjectURL(_this.file);
27373                 
27374                 _this.loadCanvas(url);
27375                 
27376                 return;
27377             }
27378             
27379             reader.readAsArrayBuffer(this.file);
27380             
27381         }
27382         
27383     },
27384     
27385     parseExifData : function(dataView, offset, length)
27386     {
27387         var tiffOffset = offset + 10,
27388             littleEndian,
27389             dirOffset;
27390     
27391         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27392             // No Exif data, might be XMP data instead
27393             return;
27394         }
27395         
27396         // Check for the ASCII code for "Exif" (0x45786966):
27397         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27398             // No Exif data, might be XMP data instead
27399             return;
27400         }
27401         if (tiffOffset + 8 > dataView.byteLength) {
27402             Roo.log('Invalid Exif data: Invalid segment size.');
27403             return;
27404         }
27405         // Check for the two null bytes:
27406         if (dataView.getUint16(offset + 8) !== 0x0000) {
27407             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27408             return;
27409         }
27410         // Check the byte alignment:
27411         switch (dataView.getUint16(tiffOffset)) {
27412         case 0x4949:
27413             littleEndian = true;
27414             break;
27415         case 0x4D4D:
27416             littleEndian = false;
27417             break;
27418         default:
27419             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27420             return;
27421         }
27422         // Check for the TIFF tag marker (0x002A):
27423         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27424             Roo.log('Invalid Exif data: Missing TIFF marker.');
27425             return;
27426         }
27427         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27428         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27429         
27430         this.parseExifTags(
27431             dataView,
27432             tiffOffset,
27433             tiffOffset + dirOffset,
27434             littleEndian
27435         );
27436     },
27437     
27438     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27439     {
27440         var tagsNumber,
27441             dirEndOffset,
27442             i;
27443         if (dirOffset + 6 > dataView.byteLength) {
27444             Roo.log('Invalid Exif data: Invalid directory offset.');
27445             return;
27446         }
27447         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27448         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27449         if (dirEndOffset + 4 > dataView.byteLength) {
27450             Roo.log('Invalid Exif data: Invalid directory size.');
27451             return;
27452         }
27453         for (i = 0; i < tagsNumber; i += 1) {
27454             this.parseExifTag(
27455                 dataView,
27456                 tiffOffset,
27457                 dirOffset + 2 + 12 * i, // tag offset
27458                 littleEndian
27459             );
27460         }
27461         // Return the offset to the next directory:
27462         return dataView.getUint32(dirEndOffset, littleEndian);
27463     },
27464     
27465     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
27466     {
27467         var tag = dataView.getUint16(offset, littleEndian);
27468         
27469         this.exif[tag] = this.getExifValue(
27470             dataView,
27471             tiffOffset,
27472             offset,
27473             dataView.getUint16(offset + 2, littleEndian), // tag type
27474             dataView.getUint32(offset + 4, littleEndian), // tag length
27475             littleEndian
27476         );
27477     },
27478     
27479     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27480     {
27481         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27482             tagSize,
27483             dataOffset,
27484             values,
27485             i,
27486             str,
27487             c;
27488     
27489         if (!tagType) {
27490             Roo.log('Invalid Exif data: Invalid tag type.');
27491             return;
27492         }
27493         
27494         tagSize = tagType.size * length;
27495         // Determine if the value is contained in the dataOffset bytes,
27496         // or if the value at the dataOffset is a pointer to the actual data:
27497         dataOffset = tagSize > 4 ?
27498                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27499         if (dataOffset + tagSize > dataView.byteLength) {
27500             Roo.log('Invalid Exif data: Invalid data offset.');
27501             return;
27502         }
27503         if (length === 1) {
27504             return tagType.getValue(dataView, dataOffset, littleEndian);
27505         }
27506         values = [];
27507         for (i = 0; i < length; i += 1) {
27508             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27509         }
27510         
27511         if (tagType.ascii) {
27512             str = '';
27513             // Concatenate the chars:
27514             for (i = 0; i < values.length; i += 1) {
27515                 c = values[i];
27516                 // Ignore the terminating NULL byte(s):
27517                 if (c === '\u0000') {
27518                     break;
27519                 }
27520                 str += c;
27521             }
27522             return str;
27523         }
27524         return values;
27525     }
27526     
27527 });
27528
27529 Roo.apply(Roo.bootstrap.UploadCropbox, {
27530     tags : {
27531         'Orientation': 0x0112
27532     },
27533     
27534     Orientation: {
27535             1: 0, //'top-left',
27536 //            2: 'top-right',
27537             3: 180, //'bottom-right',
27538 //            4: 'bottom-left',
27539 //            5: 'left-top',
27540             6: 90, //'right-top',
27541 //            7: 'right-bottom',
27542             8: 270 //'left-bottom'
27543     },
27544     
27545     exifTagTypes : {
27546         // byte, 8-bit unsigned int:
27547         1: {
27548             getValue: function (dataView, dataOffset) {
27549                 return dataView.getUint8(dataOffset);
27550             },
27551             size: 1
27552         },
27553         // ascii, 8-bit byte:
27554         2: {
27555             getValue: function (dataView, dataOffset) {
27556                 return String.fromCharCode(dataView.getUint8(dataOffset));
27557             },
27558             size: 1,
27559             ascii: true
27560         },
27561         // short, 16 bit int:
27562         3: {
27563             getValue: function (dataView, dataOffset, littleEndian) {
27564                 return dataView.getUint16(dataOffset, littleEndian);
27565             },
27566             size: 2
27567         },
27568         // long, 32 bit int:
27569         4: {
27570             getValue: function (dataView, dataOffset, littleEndian) {
27571                 return dataView.getUint32(dataOffset, littleEndian);
27572             },
27573             size: 4
27574         },
27575         // rational = two long values, first is numerator, second is denominator:
27576         5: {
27577             getValue: function (dataView, dataOffset, littleEndian) {
27578                 return dataView.getUint32(dataOffset, littleEndian) /
27579                     dataView.getUint32(dataOffset + 4, littleEndian);
27580             },
27581             size: 8
27582         },
27583         // slong, 32 bit signed int:
27584         9: {
27585             getValue: function (dataView, dataOffset, littleEndian) {
27586                 return dataView.getInt32(dataOffset, littleEndian);
27587             },
27588             size: 4
27589         },
27590         // srational, two slongs, first is numerator, second is denominator:
27591         10: {
27592             getValue: function (dataView, dataOffset, littleEndian) {
27593                 return dataView.getInt32(dataOffset, littleEndian) /
27594                     dataView.getInt32(dataOffset + 4, littleEndian);
27595             },
27596             size: 8
27597         }
27598     },
27599     
27600     footer : {
27601         STANDARD : [
27602             {
27603                 tag : 'div',
27604                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27605                 action : 'rotate-left',
27606                 cn : [
27607                     {
27608                         tag : 'button',
27609                         cls : 'btn btn-default',
27610                         html : '<i class="fa fa-undo"></i>'
27611                     }
27612                 ]
27613             },
27614             {
27615                 tag : 'div',
27616                 cls : 'btn-group roo-upload-cropbox-picture',
27617                 action : 'picture',
27618                 cn : [
27619                     {
27620                         tag : 'button',
27621                         cls : 'btn btn-default',
27622                         html : '<i class="fa fa-picture-o"></i>'
27623                     }
27624                 ]
27625             },
27626             {
27627                 tag : 'div',
27628                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27629                 action : 'rotate-right',
27630                 cn : [
27631                     {
27632                         tag : 'button',
27633                         cls : 'btn btn-default',
27634                         html : '<i class="fa fa-repeat"></i>'
27635                     }
27636                 ]
27637             }
27638         ],
27639         DOCUMENT : [
27640             {
27641                 tag : 'div',
27642                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27643                 action : 'rotate-left',
27644                 cn : [
27645                     {
27646                         tag : 'button',
27647                         cls : 'btn btn-default',
27648                         html : '<i class="fa fa-undo"></i>'
27649                     }
27650                 ]
27651             },
27652             {
27653                 tag : 'div',
27654                 cls : 'btn-group roo-upload-cropbox-download',
27655                 action : 'download',
27656                 cn : [
27657                     {
27658                         tag : 'button',
27659                         cls : 'btn btn-default',
27660                         html : '<i class="fa fa-download"></i>'
27661                     }
27662                 ]
27663             },
27664             {
27665                 tag : 'div',
27666                 cls : 'btn-group roo-upload-cropbox-crop',
27667                 action : 'crop',
27668                 cn : [
27669                     {
27670                         tag : 'button',
27671                         cls : 'btn btn-default',
27672                         html : '<i class="fa fa-crop"></i>'
27673                     }
27674                 ]
27675             },
27676             {
27677                 tag : 'div',
27678                 cls : 'btn-group roo-upload-cropbox-trash',
27679                 action : 'trash',
27680                 cn : [
27681                     {
27682                         tag : 'button',
27683                         cls : 'btn btn-default',
27684                         html : '<i class="fa fa-trash"></i>'
27685                     }
27686                 ]
27687             },
27688             {
27689                 tag : 'div',
27690                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27691                 action : 'rotate-right',
27692                 cn : [
27693                     {
27694                         tag : 'button',
27695                         cls : 'btn btn-default',
27696                         html : '<i class="fa fa-repeat"></i>'
27697                     }
27698                 ]
27699             }
27700         ],
27701         ROTATOR : [
27702             {
27703                 tag : 'div',
27704                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27705                 action : 'rotate-left',
27706                 cn : [
27707                     {
27708                         tag : 'button',
27709                         cls : 'btn btn-default',
27710                         html : '<i class="fa fa-undo"></i>'
27711                     }
27712                 ]
27713             },
27714             {
27715                 tag : 'div',
27716                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27717                 action : 'rotate-right',
27718                 cn : [
27719                     {
27720                         tag : 'button',
27721                         cls : 'btn btn-default',
27722                         html : '<i class="fa fa-repeat"></i>'
27723                     }
27724                 ]
27725             }
27726         ]
27727     }
27728 });
27729
27730 /*
27731 * Licence: LGPL
27732 */
27733
27734 /**
27735  * @class Roo.bootstrap.DocumentManager
27736  * @extends Roo.bootstrap.Component
27737  * Bootstrap DocumentManager class
27738  * @cfg {String} paramName default 'imageUpload'
27739  * @cfg {String} toolTipName default 'filename'
27740  * @cfg {String} method default POST
27741  * @cfg {String} url action url
27742  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27743  * @cfg {Boolean} multiple multiple upload default true
27744  * @cfg {Number} thumbSize default 300
27745  * @cfg {String} fieldLabel
27746  * @cfg {Number} labelWidth default 4
27747  * @cfg {String} labelAlign (left|top) default left
27748  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27749 * @cfg {Number} labellg set the width of label (1-12)
27750  * @cfg {Number} labelmd set the width of label (1-12)
27751  * @cfg {Number} labelsm set the width of label (1-12)
27752  * @cfg {Number} labelxs set the width of label (1-12)
27753  * 
27754  * @constructor
27755  * Create a new DocumentManager
27756  * @param {Object} config The config object
27757  */
27758
27759 Roo.bootstrap.DocumentManager = function(config){
27760     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27761     
27762     this.files = [];
27763     this.delegates = [];
27764     
27765     this.addEvents({
27766         /**
27767          * @event initial
27768          * Fire when initial the DocumentManager
27769          * @param {Roo.bootstrap.DocumentManager} this
27770          */
27771         "initial" : true,
27772         /**
27773          * @event inspect
27774          * inspect selected file
27775          * @param {Roo.bootstrap.DocumentManager} this
27776          * @param {File} file
27777          */
27778         "inspect" : true,
27779         /**
27780          * @event exception
27781          * Fire when xhr load exception
27782          * @param {Roo.bootstrap.DocumentManager} this
27783          * @param {XMLHttpRequest} xhr
27784          */
27785         "exception" : true,
27786         /**
27787          * @event afterupload
27788          * Fire when xhr load exception
27789          * @param {Roo.bootstrap.DocumentManager} this
27790          * @param {XMLHttpRequest} xhr
27791          */
27792         "afterupload" : true,
27793         /**
27794          * @event prepare
27795          * prepare the form data
27796          * @param {Roo.bootstrap.DocumentManager} this
27797          * @param {Object} formData
27798          */
27799         "prepare" : true,
27800         /**
27801          * @event remove
27802          * Fire when remove the file
27803          * @param {Roo.bootstrap.DocumentManager} this
27804          * @param {Object} file
27805          */
27806         "remove" : true,
27807         /**
27808          * @event refresh
27809          * Fire after refresh the file
27810          * @param {Roo.bootstrap.DocumentManager} this
27811          */
27812         "refresh" : true,
27813         /**
27814          * @event click
27815          * Fire after click the image
27816          * @param {Roo.bootstrap.DocumentManager} this
27817          * @param {Object} file
27818          */
27819         "click" : true,
27820         /**
27821          * @event edit
27822          * Fire when upload a image and editable set to true
27823          * @param {Roo.bootstrap.DocumentManager} this
27824          * @param {Object} file
27825          */
27826         "edit" : true,
27827         /**
27828          * @event beforeselectfile
27829          * Fire before select file
27830          * @param {Roo.bootstrap.DocumentManager} this
27831          */
27832         "beforeselectfile" : true,
27833         /**
27834          * @event process
27835          * Fire before process file
27836          * @param {Roo.bootstrap.DocumentManager} this
27837          * @param {Object} file
27838          */
27839         "process" : true
27840         
27841     });
27842 };
27843
27844 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
27845     
27846     boxes : 0,
27847     inputName : '',
27848     thumbSize : 300,
27849     multiple : true,
27850     files : false,
27851     method : 'POST',
27852     url : '',
27853     paramName : 'imageUpload',
27854     toolTipName : 'filename',
27855     fieldLabel : '',
27856     labelWidth : 4,
27857     labelAlign : 'left',
27858     editable : true,
27859     delegates : false,
27860     xhr : false, 
27861     
27862     labellg : 0,
27863     labelmd : 0,
27864     labelsm : 0,
27865     labelxs : 0,
27866     
27867     getAutoCreate : function()
27868     {   
27869         var managerWidget = {
27870             tag : 'div',
27871             cls : 'roo-document-manager',
27872             cn : [
27873                 {
27874                     tag : 'input',
27875                     cls : 'roo-document-manager-selector',
27876                     type : 'file'
27877                 },
27878                 {
27879                     tag : 'div',
27880                     cls : 'roo-document-manager-uploader',
27881                     cn : [
27882                         {
27883                             tag : 'div',
27884                             cls : 'roo-document-manager-upload-btn',
27885                             html : '<i class="fa fa-plus"></i>'
27886                         }
27887                     ]
27888                     
27889                 }
27890             ]
27891         };
27892         
27893         var content = [
27894             {
27895                 tag : 'div',
27896                 cls : 'column col-md-12',
27897                 cn : managerWidget
27898             }
27899         ];
27900         
27901         if(this.fieldLabel.length){
27902             
27903             content = [
27904                 {
27905                     tag : 'div',
27906                     cls : 'column col-md-12',
27907                     html : this.fieldLabel
27908                 },
27909                 {
27910                     tag : 'div',
27911                     cls : 'column col-md-12',
27912                     cn : managerWidget
27913                 }
27914             ];
27915
27916             if(this.labelAlign == 'left'){
27917                 content = [
27918                     {
27919                         tag : 'div',
27920                         cls : 'column',
27921                         html : this.fieldLabel
27922                     },
27923                     {
27924                         tag : 'div',
27925                         cls : 'column',
27926                         cn : managerWidget
27927                     }
27928                 ];
27929                 
27930                 if(this.labelWidth > 12){
27931                     content[0].style = "width: " + this.labelWidth + 'px';
27932                 }
27933
27934                 if(this.labelWidth < 13 && this.labelmd == 0){
27935                     this.labelmd = this.labelWidth;
27936                 }
27937
27938                 if(this.labellg > 0){
27939                     content[0].cls += ' col-lg-' + this.labellg;
27940                     content[1].cls += ' col-lg-' + (12 - this.labellg);
27941                 }
27942
27943                 if(this.labelmd > 0){
27944                     content[0].cls += ' col-md-' + this.labelmd;
27945                     content[1].cls += ' col-md-' + (12 - this.labelmd);
27946                 }
27947
27948                 if(this.labelsm > 0){
27949                     content[0].cls += ' col-sm-' + this.labelsm;
27950                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
27951                 }
27952
27953                 if(this.labelxs > 0){
27954                     content[0].cls += ' col-xs-' + this.labelxs;
27955                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
27956                 }
27957                 
27958             }
27959         }
27960         
27961         var cfg = {
27962             tag : 'div',
27963             cls : 'row clearfix',
27964             cn : content
27965         };
27966         
27967         return cfg;
27968         
27969     },
27970     
27971     initEvents : function()
27972     {
27973         this.managerEl = this.el.select('.roo-document-manager', true).first();
27974         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27975         
27976         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27977         this.selectorEl.hide();
27978         
27979         if(this.multiple){
27980             this.selectorEl.attr('multiple', 'multiple');
27981         }
27982         
27983         this.selectorEl.on('change', this.onFileSelected, this);
27984         
27985         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27986         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27987         
27988         this.uploader.on('click', this.onUploaderClick, this);
27989         
27990         this.renderProgressDialog();
27991         
27992         var _this = this;
27993         
27994         window.addEventListener("resize", function() { _this.refresh(); } );
27995         
27996         this.fireEvent('initial', this);
27997     },
27998     
27999     renderProgressDialog : function()
28000     {
28001         var _this = this;
28002         
28003         this.progressDialog = new Roo.bootstrap.Modal({
28004             cls : 'roo-document-manager-progress-dialog',
28005             allow_close : false,
28006             title : '',
28007             buttons : [
28008                 {
28009                     name  :'cancel',
28010                     weight : 'danger',
28011                     html : 'Cancel'
28012                 }
28013             ], 
28014             listeners : { 
28015                 btnclick : function() {
28016                     _this.uploadCancel();
28017                     this.hide();
28018                 }
28019             }
28020         });
28021          
28022         this.progressDialog.render(Roo.get(document.body));
28023          
28024         this.progress = new Roo.bootstrap.Progress({
28025             cls : 'roo-document-manager-progress',
28026             active : true,
28027             striped : true
28028         });
28029         
28030         this.progress.render(this.progressDialog.getChildContainer());
28031         
28032         this.progressBar = new Roo.bootstrap.ProgressBar({
28033             cls : 'roo-document-manager-progress-bar',
28034             aria_valuenow : 0,
28035             aria_valuemin : 0,
28036             aria_valuemax : 12,
28037             panel : 'success'
28038         });
28039         
28040         this.progressBar.render(this.progress.getChildContainer());
28041     },
28042     
28043     onUploaderClick : function(e)
28044     {
28045         e.preventDefault();
28046      
28047         if(this.fireEvent('beforeselectfile', this) != false){
28048             this.selectorEl.dom.click();
28049         }
28050         
28051     },
28052     
28053     onFileSelected : function(e)
28054     {
28055         e.preventDefault();
28056         
28057         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28058             return;
28059         }
28060         
28061         Roo.each(this.selectorEl.dom.files, function(file){
28062             if(this.fireEvent('inspect', this, file) != false){
28063                 this.files.push(file);
28064             }
28065         }, this);
28066         
28067         this.queue();
28068         
28069     },
28070     
28071     queue : function()
28072     {
28073         this.selectorEl.dom.value = '';
28074         
28075         if(!this.files.length){
28076             return;
28077         }
28078         
28079         if(this.boxes > 0 && this.files.length > this.boxes){
28080             this.files = this.files.slice(0, this.boxes);
28081         }
28082         
28083         this.uploader.show();
28084         
28085         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28086             this.uploader.hide();
28087         }
28088         
28089         var _this = this;
28090         
28091         var files = [];
28092         
28093         var docs = [];
28094         
28095         Roo.each(this.files, function(file){
28096             
28097             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28098                 var f = this.renderPreview(file);
28099                 files.push(f);
28100                 return;
28101             }
28102             
28103             if(file.type.indexOf('image') != -1){
28104                 this.delegates.push(
28105                     (function(){
28106                         _this.process(file);
28107                     }).createDelegate(this)
28108                 );
28109         
28110                 return;
28111             }
28112             
28113             docs.push(
28114                 (function(){
28115                     _this.process(file);
28116                 }).createDelegate(this)
28117             );
28118             
28119         }, this);
28120         
28121         this.files = files;
28122         
28123         this.delegates = this.delegates.concat(docs);
28124         
28125         if(!this.delegates.length){
28126             this.refresh();
28127             return;
28128         }
28129         
28130         this.progressBar.aria_valuemax = this.delegates.length;
28131         
28132         this.arrange();
28133         
28134         return;
28135     },
28136     
28137     arrange : function()
28138     {
28139         if(!this.delegates.length){
28140             this.progressDialog.hide();
28141             this.refresh();
28142             return;
28143         }
28144         
28145         var delegate = this.delegates.shift();
28146         
28147         this.progressDialog.show();
28148         
28149         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28150         
28151         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28152         
28153         delegate();
28154     },
28155     
28156     refresh : function()
28157     {
28158         this.uploader.show();
28159         
28160         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28161             this.uploader.hide();
28162         }
28163         
28164         Roo.isTouch ? this.closable(false) : this.closable(true);
28165         
28166         this.fireEvent('refresh', this);
28167     },
28168     
28169     onRemove : function(e, el, o)
28170     {
28171         e.preventDefault();
28172         
28173         this.fireEvent('remove', this, o);
28174         
28175     },
28176     
28177     remove : function(o)
28178     {
28179         var files = [];
28180         
28181         Roo.each(this.files, function(file){
28182             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28183                 files.push(file);
28184                 return;
28185             }
28186
28187             o.target.remove();
28188
28189         }, this);
28190         
28191         this.files = files;
28192         
28193         this.refresh();
28194     },
28195     
28196     clear : function()
28197     {
28198         Roo.each(this.files, function(file){
28199             if(!file.target){
28200                 return;
28201             }
28202             
28203             file.target.remove();
28204
28205         }, this);
28206         
28207         this.files = [];
28208         
28209         this.refresh();
28210     },
28211     
28212     onClick : function(e, el, o)
28213     {
28214         e.preventDefault();
28215         
28216         this.fireEvent('click', this, o);
28217         
28218     },
28219     
28220     closable : function(closable)
28221     {
28222         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28223             
28224             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28225             
28226             if(closable){
28227                 el.show();
28228                 return;
28229             }
28230             
28231             el.hide();
28232             
28233         }, this);
28234     },
28235     
28236     xhrOnLoad : function(xhr)
28237     {
28238         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28239             el.remove();
28240         }, this);
28241         
28242         if (xhr.readyState !== 4) {
28243             this.arrange();
28244             this.fireEvent('exception', this, xhr);
28245             return;
28246         }
28247
28248         var response = Roo.decode(xhr.responseText);
28249         
28250         if(!response.success){
28251             this.arrange();
28252             this.fireEvent('exception', this, xhr);
28253             return;
28254         }
28255         
28256         var file = this.renderPreview(response.data);
28257         
28258         this.files.push(file);
28259         
28260         this.arrange();
28261         
28262         this.fireEvent('afterupload', this, xhr);
28263         
28264     },
28265     
28266     xhrOnError : function(xhr)
28267     {
28268         Roo.log('xhr on error');
28269         
28270         var response = Roo.decode(xhr.responseText);
28271           
28272         Roo.log(response);
28273         
28274         this.arrange();
28275     },
28276     
28277     process : function(file)
28278     {
28279         if(this.fireEvent('process', this, file) !== false){
28280             if(this.editable && file.type.indexOf('image') != -1){
28281                 this.fireEvent('edit', this, file);
28282                 return;
28283             }
28284
28285             this.uploadStart(file, false);
28286
28287             return;
28288         }
28289         
28290     },
28291     
28292     uploadStart : function(file, crop)
28293     {
28294         this.xhr = new XMLHttpRequest();
28295         
28296         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28297             this.arrange();
28298             return;
28299         }
28300         
28301         file.xhr = this.xhr;
28302             
28303         this.managerEl.createChild({
28304             tag : 'div',
28305             cls : 'roo-document-manager-loading',
28306             cn : [
28307                 {
28308                     tag : 'div',
28309                     tooltip : file.name,
28310                     cls : 'roo-document-manager-thumb',
28311                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28312                 }
28313             ]
28314
28315         });
28316
28317         this.xhr.open(this.method, this.url, true);
28318         
28319         var headers = {
28320             "Accept": "application/json",
28321             "Cache-Control": "no-cache",
28322             "X-Requested-With": "XMLHttpRequest"
28323         };
28324         
28325         for (var headerName in headers) {
28326             var headerValue = headers[headerName];
28327             if (headerValue) {
28328                 this.xhr.setRequestHeader(headerName, headerValue);
28329             }
28330         }
28331         
28332         var _this = this;
28333         
28334         this.xhr.onload = function()
28335         {
28336             _this.xhrOnLoad(_this.xhr);
28337         }
28338         
28339         this.xhr.onerror = function()
28340         {
28341             _this.xhrOnError(_this.xhr);
28342         }
28343         
28344         var formData = new FormData();
28345
28346         formData.append('returnHTML', 'NO');
28347         
28348         if(crop){
28349             formData.append('crop', crop);
28350         }
28351         
28352         formData.append(this.paramName, file, file.name);
28353         
28354         var options = {
28355             file : file, 
28356             manually : false
28357         };
28358         
28359         if(this.fireEvent('prepare', this, formData, options) != false){
28360             
28361             if(options.manually){
28362                 return;
28363             }
28364             
28365             this.xhr.send(formData);
28366             return;
28367         };
28368         
28369         this.uploadCancel();
28370     },
28371     
28372     uploadCancel : function()
28373     {
28374         if (this.xhr) {
28375             this.xhr.abort();
28376         }
28377         
28378         this.delegates = [];
28379         
28380         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28381             el.remove();
28382         }, this);
28383         
28384         this.arrange();
28385     },
28386     
28387     renderPreview : function(file)
28388     {
28389         if(typeof(file.target) != 'undefined' && file.target){
28390             return file;
28391         }
28392         
28393         var previewEl = this.managerEl.createChild({
28394             tag : 'div',
28395             cls : 'roo-document-manager-preview',
28396             cn : [
28397                 {
28398                     tag : 'div',
28399                     tooltip : file[this.toolTipName],
28400                     cls : 'roo-document-manager-thumb',
28401                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
28402                 },
28403                 {
28404                     tag : 'button',
28405                     cls : 'close',
28406                     html : '<i class="fa fa-times-circle"></i>'
28407                 }
28408             ]
28409         });
28410
28411         var close = previewEl.select('button.close', true).first();
28412
28413         close.on('click', this.onRemove, this, file);
28414
28415         file.target = previewEl;
28416
28417         var image = previewEl.select('img', true).first();
28418         
28419         var _this = this;
28420         
28421         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28422         
28423         image.on('click', this.onClick, this, file);
28424         
28425         return file;
28426         
28427     },
28428     
28429     onPreviewLoad : function(file, image)
28430     {
28431         if(typeof(file.target) == 'undefined' || !file.target){
28432             return;
28433         }
28434         
28435         var width = image.dom.naturalWidth || image.dom.width;
28436         var height = image.dom.naturalHeight || image.dom.height;
28437         
28438         if(width > height){
28439             file.target.addClass('wide');
28440             return;
28441         }
28442         
28443         file.target.addClass('tall');
28444         return;
28445         
28446     },
28447     
28448     uploadFromSource : function(file, crop)
28449     {
28450         this.xhr = new XMLHttpRequest();
28451         
28452         this.managerEl.createChild({
28453             tag : 'div',
28454             cls : 'roo-document-manager-loading',
28455             cn : [
28456                 {
28457                     tag : 'div',
28458                     tooltip : file.name,
28459                     cls : 'roo-document-manager-thumb',
28460                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28461                 }
28462             ]
28463
28464         });
28465
28466         this.xhr.open(this.method, this.url, true);
28467         
28468         var headers = {
28469             "Accept": "application/json",
28470             "Cache-Control": "no-cache",
28471             "X-Requested-With": "XMLHttpRequest"
28472         };
28473         
28474         for (var headerName in headers) {
28475             var headerValue = headers[headerName];
28476             if (headerValue) {
28477                 this.xhr.setRequestHeader(headerName, headerValue);
28478             }
28479         }
28480         
28481         var _this = this;
28482         
28483         this.xhr.onload = function()
28484         {
28485             _this.xhrOnLoad(_this.xhr);
28486         }
28487         
28488         this.xhr.onerror = function()
28489         {
28490             _this.xhrOnError(_this.xhr);
28491         }
28492         
28493         var formData = new FormData();
28494
28495         formData.append('returnHTML', 'NO');
28496         
28497         formData.append('crop', crop);
28498         
28499         if(typeof(file.filename) != 'undefined'){
28500             formData.append('filename', file.filename);
28501         }
28502         
28503         if(typeof(file.mimetype) != 'undefined'){
28504             formData.append('mimetype', file.mimetype);
28505         }
28506         
28507         Roo.log(formData);
28508         
28509         if(this.fireEvent('prepare', this, formData) != false){
28510             this.xhr.send(formData);
28511         };
28512     }
28513 });
28514
28515 /*
28516 * Licence: LGPL
28517 */
28518
28519 /**
28520  * @class Roo.bootstrap.DocumentViewer
28521  * @extends Roo.bootstrap.Component
28522  * Bootstrap DocumentViewer class
28523  * @cfg {Boolean} showDownload (true|false) show download button (default true)
28524  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28525  * 
28526  * @constructor
28527  * Create a new DocumentViewer
28528  * @param {Object} config The config object
28529  */
28530
28531 Roo.bootstrap.DocumentViewer = function(config){
28532     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28533     
28534     this.addEvents({
28535         /**
28536          * @event initial
28537          * Fire after initEvent
28538          * @param {Roo.bootstrap.DocumentViewer} this
28539          */
28540         "initial" : true,
28541         /**
28542          * @event click
28543          * Fire after click
28544          * @param {Roo.bootstrap.DocumentViewer} this
28545          */
28546         "click" : true,
28547         /**
28548          * @event download
28549          * Fire after download button
28550          * @param {Roo.bootstrap.DocumentViewer} this
28551          */
28552         "download" : true,
28553         /**
28554          * @event trash
28555          * Fire after trash button
28556          * @param {Roo.bootstrap.DocumentViewer} this
28557          */
28558         "trash" : true
28559         
28560     });
28561 };
28562
28563 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
28564     
28565     showDownload : true,
28566     
28567     showTrash : true,
28568     
28569     getAutoCreate : function()
28570     {
28571         var cfg = {
28572             tag : 'div',
28573             cls : 'roo-document-viewer',
28574             cn : [
28575                 {
28576                     tag : 'div',
28577                     cls : 'roo-document-viewer-body',
28578                     cn : [
28579                         {
28580                             tag : 'div',
28581                             cls : 'roo-document-viewer-thumb',
28582                             cn : [
28583                                 {
28584                                     tag : 'img',
28585                                     cls : 'roo-document-viewer-image'
28586                                 }
28587                             ]
28588                         }
28589                     ]
28590                 },
28591                 {
28592                     tag : 'div',
28593                     cls : 'roo-document-viewer-footer',
28594                     cn : {
28595                         tag : 'div',
28596                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
28597                         cn : [
28598                             {
28599                                 tag : 'div',
28600                                 cls : 'btn-group roo-document-viewer-download',
28601                                 cn : [
28602                                     {
28603                                         tag : 'button',
28604                                         cls : 'btn btn-default',
28605                                         html : '<i class="fa fa-download"></i>'
28606                                     }
28607                                 ]
28608                             },
28609                             {
28610                                 tag : 'div',
28611                                 cls : 'btn-group roo-document-viewer-trash',
28612                                 cn : [
28613                                     {
28614                                         tag : 'button',
28615                                         cls : 'btn btn-default',
28616                                         html : '<i class="fa fa-trash"></i>'
28617                                     }
28618                                 ]
28619                             }
28620                         ]
28621                     }
28622                 }
28623             ]
28624         };
28625         
28626         return cfg;
28627     },
28628     
28629     initEvents : function()
28630     {
28631         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
28632         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
28633         
28634         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
28635         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
28636         
28637         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
28638         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
28639         
28640         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
28641         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
28642         
28643         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
28644         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
28645         
28646         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
28647         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
28648         
28649         this.bodyEl.on('click', this.onClick, this);
28650         this.downloadBtn.on('click', this.onDownload, this);
28651         this.trashBtn.on('click', this.onTrash, this);
28652         
28653         this.downloadBtn.hide();
28654         this.trashBtn.hide();
28655         
28656         if(this.showDownload){
28657             this.downloadBtn.show();
28658         }
28659         
28660         if(this.showTrash){
28661             this.trashBtn.show();
28662         }
28663         
28664         if(!this.showDownload && !this.showTrash) {
28665             this.footerEl.hide();
28666         }
28667         
28668     },
28669     
28670     initial : function()
28671     {
28672         this.fireEvent('initial', this);
28673         
28674     },
28675     
28676     onClick : function(e)
28677     {
28678         e.preventDefault();
28679         
28680         this.fireEvent('click', this);
28681     },
28682     
28683     onDownload : function(e)
28684     {
28685         e.preventDefault();
28686         
28687         this.fireEvent('download', this);
28688     },
28689     
28690     onTrash : function(e)
28691     {
28692         e.preventDefault();
28693         
28694         this.fireEvent('trash', this);
28695     }
28696     
28697 });
28698 /*
28699  * - LGPL
28700  *
28701  * nav progress bar
28702  * 
28703  */
28704
28705 /**
28706  * @class Roo.bootstrap.NavProgressBar
28707  * @extends Roo.bootstrap.Component
28708  * Bootstrap NavProgressBar class
28709  * 
28710  * @constructor
28711  * Create a new nav progress bar
28712  * @param {Object} config The config object
28713  */
28714
28715 Roo.bootstrap.NavProgressBar = function(config){
28716     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
28717
28718     this.bullets = this.bullets || [];
28719    
28720 //    Roo.bootstrap.NavProgressBar.register(this);
28721      this.addEvents({
28722         /**
28723              * @event changed
28724              * Fires when the active item changes
28725              * @param {Roo.bootstrap.NavProgressBar} this
28726              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28727              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
28728          */
28729         'changed': true
28730      });
28731     
28732 };
28733
28734 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
28735     
28736     bullets : [],
28737     barItems : [],
28738     
28739     getAutoCreate : function()
28740     {
28741         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28742         
28743         cfg = {
28744             tag : 'div',
28745             cls : 'roo-navigation-bar-group',
28746             cn : [
28747                 {
28748                     tag : 'div',
28749                     cls : 'roo-navigation-top-bar'
28750                 },
28751                 {
28752                     tag : 'div',
28753                     cls : 'roo-navigation-bullets-bar',
28754                     cn : [
28755                         {
28756                             tag : 'ul',
28757                             cls : 'roo-navigation-bar'
28758                         }
28759                     ]
28760                 },
28761                 
28762                 {
28763                     tag : 'div',
28764                     cls : 'roo-navigation-bottom-bar'
28765                 }
28766             ]
28767             
28768         };
28769         
28770         return cfg;
28771         
28772     },
28773     
28774     initEvents: function() 
28775     {
28776         
28777     },
28778     
28779     onRender : function(ct, position) 
28780     {
28781         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28782         
28783         if(this.bullets.length){
28784             Roo.each(this.bullets, function(b){
28785                this.addItem(b);
28786             }, this);
28787         }
28788         
28789         this.format();
28790         
28791     },
28792     
28793     addItem : function(cfg)
28794     {
28795         var item = new Roo.bootstrap.NavProgressItem(cfg);
28796         
28797         item.parentId = this.id;
28798         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28799         
28800         if(cfg.html){
28801             var top = new Roo.bootstrap.Element({
28802                 tag : 'div',
28803                 cls : 'roo-navigation-bar-text'
28804             });
28805             
28806             var bottom = new Roo.bootstrap.Element({
28807                 tag : 'div',
28808                 cls : 'roo-navigation-bar-text'
28809             });
28810             
28811             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28812             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28813             
28814             var topText = new Roo.bootstrap.Element({
28815                 tag : 'span',
28816                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28817             });
28818             
28819             var bottomText = new Roo.bootstrap.Element({
28820                 tag : 'span',
28821                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28822             });
28823             
28824             topText.onRender(top.el, null);
28825             bottomText.onRender(bottom.el, null);
28826             
28827             item.topEl = top;
28828             item.bottomEl = bottom;
28829         }
28830         
28831         this.barItems.push(item);
28832         
28833         return item;
28834     },
28835     
28836     getActive : function()
28837     {
28838         var active = false;
28839         
28840         Roo.each(this.barItems, function(v){
28841             
28842             if (!v.isActive()) {
28843                 return;
28844             }
28845             
28846             active = v;
28847             return false;
28848             
28849         });
28850         
28851         return active;
28852     },
28853     
28854     setActiveItem : function(item)
28855     {
28856         var prev = false;
28857         
28858         Roo.each(this.barItems, function(v){
28859             if (v.rid == item.rid) {
28860                 return ;
28861             }
28862             
28863             if (v.isActive()) {
28864                 v.setActive(false);
28865                 prev = v;
28866             }
28867         });
28868
28869         item.setActive(true);
28870         
28871         this.fireEvent('changed', this, item, prev);
28872     },
28873     
28874     getBarItem: function(rid)
28875     {
28876         var ret = false;
28877         
28878         Roo.each(this.barItems, function(e) {
28879             if (e.rid != rid) {
28880                 return;
28881             }
28882             
28883             ret =  e;
28884             return false;
28885         });
28886         
28887         return ret;
28888     },
28889     
28890     indexOfItem : function(item)
28891     {
28892         var index = false;
28893         
28894         Roo.each(this.barItems, function(v, i){
28895             
28896             if (v.rid != item.rid) {
28897                 return;
28898             }
28899             
28900             index = i;
28901             return false
28902         });
28903         
28904         return index;
28905     },
28906     
28907     setActiveNext : function()
28908     {
28909         var i = this.indexOfItem(this.getActive());
28910         
28911         if (i > this.barItems.length) {
28912             return;
28913         }
28914         
28915         this.setActiveItem(this.barItems[i+1]);
28916     },
28917     
28918     setActivePrev : function()
28919     {
28920         var i = this.indexOfItem(this.getActive());
28921         
28922         if (i  < 1) {
28923             return;
28924         }
28925         
28926         this.setActiveItem(this.barItems[i-1]);
28927     },
28928     
28929     format : function()
28930     {
28931         if(!this.barItems.length){
28932             return;
28933         }
28934      
28935         var width = 100 / this.barItems.length;
28936         
28937         Roo.each(this.barItems, function(i){
28938             i.el.setStyle('width', width + '%');
28939             i.topEl.el.setStyle('width', width + '%');
28940             i.bottomEl.el.setStyle('width', width + '%');
28941         }, this);
28942         
28943     }
28944     
28945 });
28946 /*
28947  * - LGPL
28948  *
28949  * Nav Progress Item
28950  * 
28951  */
28952
28953 /**
28954  * @class Roo.bootstrap.NavProgressItem
28955  * @extends Roo.bootstrap.Component
28956  * Bootstrap NavProgressItem class
28957  * @cfg {String} rid the reference id
28958  * @cfg {Boolean} active (true|false) Is item active default false
28959  * @cfg {Boolean} disabled (true|false) Is item active default false
28960  * @cfg {String} html
28961  * @cfg {String} position (top|bottom) text position default bottom
28962  * @cfg {String} icon show icon instead of number
28963  * 
28964  * @constructor
28965  * Create a new NavProgressItem
28966  * @param {Object} config The config object
28967  */
28968 Roo.bootstrap.NavProgressItem = function(config){
28969     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28970     this.addEvents({
28971         // raw events
28972         /**
28973          * @event click
28974          * The raw click event for the entire grid.
28975          * @param {Roo.bootstrap.NavProgressItem} this
28976          * @param {Roo.EventObject} e
28977          */
28978         "click" : true
28979     });
28980    
28981 };
28982
28983 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
28984     
28985     rid : '',
28986     active : false,
28987     disabled : false,
28988     html : '',
28989     position : 'bottom',
28990     icon : false,
28991     
28992     getAutoCreate : function()
28993     {
28994         var iconCls = 'roo-navigation-bar-item-icon';
28995         
28996         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28997         
28998         var cfg = {
28999             tag: 'li',
29000             cls: 'roo-navigation-bar-item',
29001             cn : [
29002                 {
29003                     tag : 'i',
29004                     cls : iconCls
29005                 }
29006             ]
29007         };
29008         
29009         if(this.active){
29010             cfg.cls += ' active';
29011         }
29012         if(this.disabled){
29013             cfg.cls += ' disabled';
29014         }
29015         
29016         return cfg;
29017     },
29018     
29019     disable : function()
29020     {
29021         this.setDisabled(true);
29022     },
29023     
29024     enable : function()
29025     {
29026         this.setDisabled(false);
29027     },
29028     
29029     initEvents: function() 
29030     {
29031         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29032         
29033         this.iconEl.on('click', this.onClick, this);
29034     },
29035     
29036     onClick : function(e)
29037     {
29038         e.preventDefault();
29039         
29040         if(this.disabled){
29041             return;
29042         }
29043         
29044         if(this.fireEvent('click', this, e) === false){
29045             return;
29046         };
29047         
29048         this.parent().setActiveItem(this);
29049     },
29050     
29051     isActive: function () 
29052     {
29053         return this.active;
29054     },
29055     
29056     setActive : function(state)
29057     {
29058         if(this.active == state){
29059             return;
29060         }
29061         
29062         this.active = state;
29063         
29064         if (state) {
29065             this.el.addClass('active');
29066             return;
29067         }
29068         
29069         this.el.removeClass('active');
29070         
29071         return;
29072     },
29073     
29074     setDisabled : function(state)
29075     {
29076         if(this.disabled == state){
29077             return;
29078         }
29079         
29080         this.disabled = state;
29081         
29082         if (state) {
29083             this.el.addClass('disabled');
29084             return;
29085         }
29086         
29087         this.el.removeClass('disabled');
29088     },
29089     
29090     tooltipEl : function()
29091     {
29092         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29093     }
29094 });
29095  
29096
29097  /*
29098  * - LGPL
29099  *
29100  * FieldLabel
29101  * 
29102  */
29103
29104 /**
29105  * @class Roo.bootstrap.FieldLabel
29106  * @extends Roo.bootstrap.Component
29107  * Bootstrap FieldLabel class
29108  * @cfg {String} html contents of the element
29109  * @cfg {String} tag tag of the element default label
29110  * @cfg {String} cls class of the element
29111  * @cfg {String} target label target 
29112  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29113  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
29114  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
29115  * @cfg {String} iconTooltip default "This field is required"
29116  * 
29117  * @constructor
29118  * Create a new FieldLabel
29119  * @param {Object} config The config object
29120  */
29121
29122 Roo.bootstrap.FieldLabel = function(config){
29123     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29124     
29125     this.addEvents({
29126             /**
29127              * @event invalid
29128              * Fires after the field has been marked as invalid.
29129              * @param {Roo.form.FieldLabel} this
29130              * @param {String} msg The validation message
29131              */
29132             invalid : true,
29133             /**
29134              * @event valid
29135              * Fires after the field has been validated with no errors.
29136              * @param {Roo.form.FieldLabel} this
29137              */
29138             valid : true
29139         });
29140 };
29141
29142 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29143     
29144     tag: 'label',
29145     cls: '',
29146     html: '',
29147     target: '',
29148     allowBlank : true,
29149     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
29150     validClass : 'text-success fa fa-lg fa-check',
29151     iconTooltip : 'This field is required',
29152     
29153     getAutoCreate : function(){
29154         
29155         var cfg = {
29156             tag : this.tag,
29157             cls : 'roo-bootstrap-field-label ' + this.cls,
29158             for : this.target,
29159             cn : [
29160                 {
29161                     tag : 'i',
29162                     cls : '',
29163                     tooltip : this.iconTooltip
29164                 },
29165                 {
29166                     tag : 'span',
29167                     html : this.html
29168                 }
29169             ] 
29170         };
29171         
29172         return cfg;
29173     },
29174     
29175     initEvents: function() 
29176     {
29177         Roo.bootstrap.Element.superclass.initEvents.call(this);
29178         
29179         this.iconEl = this.el.select('i', true).first();
29180         
29181         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
29182         
29183         Roo.bootstrap.FieldLabel.register(this);
29184     },
29185     
29186     /**
29187      * Mark this field as valid
29188      */
29189     markValid : function()
29190     {
29191         this.iconEl.show();
29192         
29193         this.iconEl.removeClass(this.invalidClass);
29194         
29195         this.iconEl.addClass(this.validClass);
29196         
29197         this.fireEvent('valid', this);
29198     },
29199     
29200     /**
29201      * Mark this field as invalid
29202      * @param {String} msg The validation message
29203      */
29204     markInvalid : function(msg)
29205     {
29206         this.iconEl.show();
29207         
29208         this.iconEl.removeClass(this.validClass);
29209         
29210         this.iconEl.addClass(this.invalidClass);
29211         
29212         this.fireEvent('invalid', this, msg);
29213     }
29214     
29215    
29216 });
29217
29218 Roo.apply(Roo.bootstrap.FieldLabel, {
29219     
29220     groups: {},
29221     
29222      /**
29223     * register a FieldLabel Group
29224     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29225     */
29226     register : function(label)
29227     {
29228         if(this.groups.hasOwnProperty(label.target)){
29229             return;
29230         }
29231      
29232         this.groups[label.target] = label;
29233         
29234     },
29235     /**
29236     * fetch a FieldLabel Group based on the target
29237     * @param {string} target
29238     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29239     */
29240     get: function(target) {
29241         if (typeof(this.groups[target]) == 'undefined') {
29242             return false;
29243         }
29244         
29245         return this.groups[target] ;
29246     }
29247 });
29248
29249  
29250
29251  /*
29252  * - LGPL
29253  *
29254  * page DateSplitField.
29255  * 
29256  */
29257
29258
29259 /**
29260  * @class Roo.bootstrap.DateSplitField
29261  * @extends Roo.bootstrap.Component
29262  * Bootstrap DateSplitField class
29263  * @cfg {string} fieldLabel - the label associated
29264  * @cfg {Number} labelWidth set the width of label (0-12)
29265  * @cfg {String} labelAlign (top|left)
29266  * @cfg {Boolean} dayAllowBlank (true|false) default false
29267  * @cfg {Boolean} monthAllowBlank (true|false) default false
29268  * @cfg {Boolean} yearAllowBlank (true|false) default false
29269  * @cfg {string} dayPlaceholder 
29270  * @cfg {string} monthPlaceholder
29271  * @cfg {string} yearPlaceholder
29272  * @cfg {string} dayFormat default 'd'
29273  * @cfg {string} monthFormat default 'm'
29274  * @cfg {string} yearFormat default 'Y'
29275  * @cfg {Number} labellg set the width of label (1-12)
29276  * @cfg {Number} labelmd set the width of label (1-12)
29277  * @cfg {Number} labelsm set the width of label (1-12)
29278  * @cfg {Number} labelxs set the width of label (1-12)
29279
29280  *     
29281  * @constructor
29282  * Create a new DateSplitField
29283  * @param {Object} config The config object
29284  */
29285
29286 Roo.bootstrap.DateSplitField = function(config){
29287     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29288     
29289     this.addEvents({
29290         // raw events
29291          /**
29292          * @event years
29293          * getting the data of years
29294          * @param {Roo.bootstrap.DateSplitField} this
29295          * @param {Object} years
29296          */
29297         "years" : true,
29298         /**
29299          * @event days
29300          * getting the data of days
29301          * @param {Roo.bootstrap.DateSplitField} this
29302          * @param {Object} days
29303          */
29304         "days" : true,
29305         /**
29306          * @event invalid
29307          * Fires after the field has been marked as invalid.
29308          * @param {Roo.form.Field} this
29309          * @param {String} msg The validation message
29310          */
29311         invalid : true,
29312        /**
29313          * @event valid
29314          * Fires after the field has been validated with no errors.
29315          * @param {Roo.form.Field} this
29316          */
29317         valid : true
29318     });
29319 };
29320
29321 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29322     
29323     fieldLabel : '',
29324     labelAlign : 'top',
29325     labelWidth : 3,
29326     dayAllowBlank : false,
29327     monthAllowBlank : false,
29328     yearAllowBlank : false,
29329     dayPlaceholder : '',
29330     monthPlaceholder : '',
29331     yearPlaceholder : '',
29332     dayFormat : 'd',
29333     monthFormat : 'm',
29334     yearFormat : 'Y',
29335     isFormField : true,
29336     labellg : 0,
29337     labelmd : 0,
29338     labelsm : 0,
29339     labelxs : 0,
29340     
29341     getAutoCreate : function()
29342     {
29343         var cfg = {
29344             tag : 'div',
29345             cls : 'row roo-date-split-field-group',
29346             cn : [
29347                 {
29348                     tag : 'input',
29349                     type : 'hidden',
29350                     cls : 'form-hidden-field roo-date-split-field-group-value',
29351                     name : this.name
29352                 }
29353             ]
29354         };
29355         
29356         var labelCls = 'col-md-12';
29357         var contentCls = 'col-md-4';
29358         
29359         if(this.fieldLabel){
29360             
29361             var label = {
29362                 tag : 'div',
29363                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29364                 cn : [
29365                     {
29366                         tag : 'label',
29367                         html : this.fieldLabel
29368                     }
29369                 ]
29370             };
29371             
29372             if(this.labelAlign == 'left'){
29373             
29374                 if(this.labelWidth > 12){
29375                     label.style = "width: " + this.labelWidth + 'px';
29376                 }
29377
29378                 if(this.labelWidth < 13 && this.labelmd == 0){
29379                     this.labelmd = this.labelWidth;
29380                 }
29381
29382                 if(this.labellg > 0){
29383                     labelCls = ' col-lg-' + this.labellg;
29384                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
29385                 }
29386
29387                 if(this.labelmd > 0){
29388                     labelCls = ' col-md-' + this.labelmd;
29389                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
29390                 }
29391
29392                 if(this.labelsm > 0){
29393                     labelCls = ' col-sm-' + this.labelsm;
29394                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
29395                 }
29396
29397                 if(this.labelxs > 0){
29398                     labelCls = ' col-xs-' + this.labelxs;
29399                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
29400                 }
29401             }
29402             
29403             label.cls += ' ' + labelCls;
29404             
29405             cfg.cn.push(label);
29406         }
29407         
29408         Roo.each(['day', 'month', 'year'], function(t){
29409             cfg.cn.push({
29410                 tag : 'div',
29411                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
29412             });
29413         }, this);
29414         
29415         return cfg;
29416     },
29417     
29418     inputEl: function ()
29419     {
29420         return this.el.select('.roo-date-split-field-group-value', true).first();
29421     },
29422     
29423     onRender : function(ct, position) 
29424     {
29425         var _this = this;
29426         
29427         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29428         
29429         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29430         
29431         this.dayField = new Roo.bootstrap.ComboBox({
29432             allowBlank : this.dayAllowBlank,
29433             alwaysQuery : true,
29434             displayField : 'value',
29435             editable : false,
29436             fieldLabel : '',
29437             forceSelection : true,
29438             mode : 'local',
29439             placeholder : this.dayPlaceholder,
29440             selectOnFocus : true,
29441             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29442             triggerAction : 'all',
29443             typeAhead : true,
29444             valueField : 'value',
29445             store : new Roo.data.SimpleStore({
29446                 data : (function() {    
29447                     var days = [];
29448                     _this.fireEvent('days', _this, days);
29449                     return days;
29450                 })(),
29451                 fields : [ 'value' ]
29452             }),
29453             listeners : {
29454                 select : function (_self, record, index)
29455                 {
29456                     _this.setValue(_this.getValue());
29457                 }
29458             }
29459         });
29460
29461         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29462         
29463         this.monthField = new Roo.bootstrap.MonthField({
29464             after : '<i class=\"fa fa-calendar\"></i>',
29465             allowBlank : this.monthAllowBlank,
29466             placeholder : this.monthPlaceholder,
29467             readOnly : true,
29468             listeners : {
29469                 render : function (_self)
29470                 {
29471                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
29472                         e.preventDefault();
29473                         _self.focus();
29474                     });
29475                 },
29476                 select : function (_self, oldvalue, newvalue)
29477                 {
29478                     _this.setValue(_this.getValue());
29479                 }
29480             }
29481         });
29482         
29483         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29484         
29485         this.yearField = new Roo.bootstrap.ComboBox({
29486             allowBlank : this.yearAllowBlank,
29487             alwaysQuery : true,
29488             displayField : 'value',
29489             editable : false,
29490             fieldLabel : '',
29491             forceSelection : true,
29492             mode : 'local',
29493             placeholder : this.yearPlaceholder,
29494             selectOnFocus : true,
29495             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29496             triggerAction : 'all',
29497             typeAhead : true,
29498             valueField : 'value',
29499             store : new Roo.data.SimpleStore({
29500                 data : (function() {
29501                     var years = [];
29502                     _this.fireEvent('years', _this, years);
29503                     return years;
29504                 })(),
29505                 fields : [ 'value' ]
29506             }),
29507             listeners : {
29508                 select : function (_self, record, index)
29509                 {
29510                     _this.setValue(_this.getValue());
29511                 }
29512             }
29513         });
29514
29515         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29516     },
29517     
29518     setValue : function(v, format)
29519     {
29520         this.inputEl.dom.value = v;
29521         
29522         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29523         
29524         var d = Date.parseDate(v, f);
29525         
29526         if(!d){
29527             this.validate();
29528             return;
29529         }
29530         
29531         this.setDay(d.format(this.dayFormat));
29532         this.setMonth(d.format(this.monthFormat));
29533         this.setYear(d.format(this.yearFormat));
29534         
29535         this.validate();
29536         
29537         return;
29538     },
29539     
29540     setDay : function(v)
29541     {
29542         this.dayField.setValue(v);
29543         this.inputEl.dom.value = this.getValue();
29544         this.validate();
29545         return;
29546     },
29547     
29548     setMonth : function(v)
29549     {
29550         this.monthField.setValue(v, true);
29551         this.inputEl.dom.value = this.getValue();
29552         this.validate();
29553         return;
29554     },
29555     
29556     setYear : function(v)
29557     {
29558         this.yearField.setValue(v);
29559         this.inputEl.dom.value = this.getValue();
29560         this.validate();
29561         return;
29562     },
29563     
29564     getDay : function()
29565     {
29566         return this.dayField.getValue();
29567     },
29568     
29569     getMonth : function()
29570     {
29571         return this.monthField.getValue();
29572     },
29573     
29574     getYear : function()
29575     {
29576         return this.yearField.getValue();
29577     },
29578     
29579     getValue : function()
29580     {
29581         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
29582         
29583         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
29584         
29585         return date;
29586     },
29587     
29588     reset : function()
29589     {
29590         this.setDay('');
29591         this.setMonth('');
29592         this.setYear('');
29593         this.inputEl.dom.value = '';
29594         this.validate();
29595         return;
29596     },
29597     
29598     validate : function()
29599     {
29600         var d = this.dayField.validate();
29601         var m = this.monthField.validate();
29602         var y = this.yearField.validate();
29603         
29604         var valid = true;
29605         
29606         if(
29607                 (!this.dayAllowBlank && !d) ||
29608                 (!this.monthAllowBlank && !m) ||
29609                 (!this.yearAllowBlank && !y)
29610         ){
29611             valid = false;
29612         }
29613         
29614         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
29615             return valid;
29616         }
29617         
29618         if(valid){
29619             this.markValid();
29620             return valid;
29621         }
29622         
29623         this.markInvalid();
29624         
29625         return valid;
29626     },
29627     
29628     markValid : function()
29629     {
29630         
29631         var label = this.el.select('label', true).first();
29632         var icon = this.el.select('i.fa-star', true).first();
29633
29634         if(label && icon){
29635             icon.remove();
29636         }
29637         
29638         this.fireEvent('valid', this);
29639     },
29640     
29641      /**
29642      * Mark this field as invalid
29643      * @param {String} msg The validation message
29644      */
29645     markInvalid : function(msg)
29646     {
29647         
29648         var label = this.el.select('label', true).first();
29649         var icon = this.el.select('i.fa-star', true).first();
29650
29651         if(label && !icon){
29652             this.el.select('.roo-date-split-field-label', true).createChild({
29653                 tag : 'i',
29654                 cls : 'text-danger fa fa-lg fa-star',
29655                 tooltip : 'This field is required',
29656                 style : 'margin-right:5px;'
29657             }, label, true);
29658         }
29659         
29660         this.fireEvent('invalid', this, msg);
29661     },
29662     
29663     clearInvalid : function()
29664     {
29665         var label = this.el.select('label', true).first();
29666         var icon = this.el.select('i.fa-star', true).first();
29667
29668         if(label && icon){
29669             icon.remove();
29670         }
29671         
29672         this.fireEvent('valid', this);
29673     },
29674     
29675     getName: function()
29676     {
29677         return this.name;
29678     }
29679     
29680 });
29681
29682  /**
29683  *
29684  * This is based on 
29685  * http://masonry.desandro.com
29686  *
29687  * The idea is to render all the bricks based on vertical width...
29688  *
29689  * The original code extends 'outlayer' - we might need to use that....
29690  * 
29691  */
29692
29693
29694 /**
29695  * @class Roo.bootstrap.LayoutMasonry
29696  * @extends Roo.bootstrap.Component
29697  * Bootstrap Layout Masonry class
29698  * 
29699  * @constructor
29700  * Create a new Element
29701  * @param {Object} config The config object
29702  */
29703
29704 Roo.bootstrap.LayoutMasonry = function(config){
29705     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
29706     
29707     this.bricks = [];
29708     
29709 };
29710
29711 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
29712     
29713     /**
29714      * @cfg {Boolean} isLayoutInstant = no animation?
29715      */   
29716     isLayoutInstant : false, // needed?
29717    
29718     /**
29719      * @cfg {Number} boxWidth  width of the columns
29720      */   
29721     boxWidth : 450,
29722     
29723       /**
29724      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
29725      */   
29726     boxHeight : 0,
29727     
29728     /**
29729      * @cfg {Number} padWidth padding below box..
29730      */   
29731     padWidth : 10, 
29732     
29733     /**
29734      * @cfg {Number} gutter gutter width..
29735      */   
29736     gutter : 10,
29737     
29738      /**
29739      * @cfg {Number} maxCols maximum number of columns
29740      */   
29741     
29742     maxCols: 0,
29743     
29744     /**
29745      * @cfg {Boolean} isAutoInitial defalut true
29746      */   
29747     isAutoInitial : true, 
29748     
29749     containerWidth: 0,
29750     
29751     /**
29752      * @cfg {Boolean} isHorizontal defalut false
29753      */   
29754     isHorizontal : false, 
29755
29756     currentSize : null,
29757     
29758     tag: 'div',
29759     
29760     cls: '',
29761     
29762     bricks: null, //CompositeElement
29763     
29764     cols : 1,
29765     
29766     _isLayoutInited : false,
29767     
29768 //    isAlternative : false, // only use for vertical layout...
29769     
29770     /**
29771      * @cfg {Number} alternativePadWidth padding below box..
29772      */   
29773     alternativePadWidth : 50, 
29774     
29775     getAutoCreate : function(){
29776         
29777         var cfg = {
29778             tag: this.tag,
29779             cls: 'blog-masonary-wrapper ' + this.cls,
29780             cn : {
29781                 cls : 'mas-boxes masonary'
29782             }
29783         };
29784         
29785         return cfg;
29786     },
29787     
29788     getChildContainer: function( )
29789     {
29790         if (this.boxesEl) {
29791             return this.boxesEl;
29792         }
29793         
29794         this.boxesEl = this.el.select('.mas-boxes').first();
29795         
29796         return this.boxesEl;
29797     },
29798     
29799     
29800     initEvents : function()
29801     {
29802         var _this = this;
29803         
29804         if(this.isAutoInitial){
29805             Roo.log('hook children rendered');
29806             this.on('childrenrendered', function() {
29807                 Roo.log('children rendered');
29808                 _this.initial();
29809             } ,this);
29810         }
29811     },
29812     
29813     initial : function()
29814     {
29815         this.currentSize = this.el.getBox(true);
29816         
29817         Roo.EventManager.onWindowResize(this.resize, this); 
29818
29819         if(!this.isAutoInitial){
29820             this.layout();
29821             return;
29822         }
29823         
29824         this.layout();
29825         
29826         return;
29827         //this.layout.defer(500,this);
29828         
29829     },
29830     
29831     resize : function()
29832     {
29833         Roo.log('resize');
29834         
29835         var cs = this.el.getBox(true);
29836         
29837         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29838             Roo.log("no change in with or X");
29839             return;
29840         }
29841         
29842         this.currentSize = cs;
29843         
29844         this.layout();
29845         
29846     },
29847     
29848     layout : function()
29849     {   
29850         this._resetLayout();
29851         
29852         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29853         
29854         this.layoutItems( isInstant );
29855       
29856         this._isLayoutInited = true;
29857         
29858     },
29859     
29860     _resetLayout : function()
29861     {
29862         if(this.isHorizontal){
29863             this.horizontalMeasureColumns();
29864             return;
29865         }
29866         
29867         this.verticalMeasureColumns();
29868         
29869     },
29870     
29871     verticalMeasureColumns : function()
29872     {
29873         this.getContainerWidth();
29874         
29875 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29876 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
29877 //            return;
29878 //        }
29879         
29880         var boxWidth = this.boxWidth + this.padWidth;
29881         
29882         if(this.containerWidth < this.boxWidth){
29883             boxWidth = this.containerWidth
29884         }
29885         
29886         var containerWidth = this.containerWidth;
29887         
29888         var cols = Math.floor(containerWidth / boxWidth);
29889         
29890         this.cols = Math.max( cols, 1 );
29891         
29892         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29893         
29894         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29895         
29896         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29897         
29898         this.colWidth = boxWidth + avail - this.padWidth;
29899         
29900         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29901         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
29902     },
29903     
29904     horizontalMeasureColumns : function()
29905     {
29906         this.getContainerWidth();
29907         
29908         var boxWidth = this.boxWidth;
29909         
29910         if(this.containerWidth < boxWidth){
29911             boxWidth = this.containerWidth;
29912         }
29913         
29914         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29915         
29916         this.el.setHeight(boxWidth);
29917         
29918     },
29919     
29920     getContainerWidth : function()
29921     {
29922         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
29923     },
29924     
29925     layoutItems : function( isInstant )
29926     {
29927         var items = Roo.apply([], this.bricks);
29928         
29929         if(this.isHorizontal){
29930             this._horizontalLayoutItems( items , isInstant );
29931             return;
29932         }
29933         
29934 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29935 //            this._verticalAlternativeLayoutItems( items , isInstant );
29936 //            return;
29937 //        }
29938         
29939         this._verticalLayoutItems( items , isInstant );
29940         
29941     },
29942     
29943     _verticalLayoutItems : function ( items , isInstant)
29944     {
29945         if ( !items || !items.length ) {
29946             return;
29947         }
29948         
29949         var standard = [
29950             ['xs', 'xs', 'xs', 'tall'],
29951             ['xs', 'xs', 'tall'],
29952             ['xs', 'xs', 'sm'],
29953             ['xs', 'xs', 'xs'],
29954             ['xs', 'tall'],
29955             ['xs', 'sm'],
29956             ['xs', 'xs'],
29957             ['xs'],
29958             
29959             ['sm', 'xs', 'xs'],
29960             ['sm', 'xs'],
29961             ['sm'],
29962             
29963             ['tall', 'xs', 'xs', 'xs'],
29964             ['tall', 'xs', 'xs'],
29965             ['tall', 'xs'],
29966             ['tall']
29967             
29968         ];
29969         
29970         var queue = [];
29971         
29972         var boxes = [];
29973         
29974         var box = [];
29975         
29976         Roo.each(items, function(item, k){
29977             
29978             switch (item.size) {
29979                 // these layouts take up a full box,
29980                 case 'md' :
29981                 case 'md-left' :
29982                 case 'md-right' :
29983                 case 'wide' :
29984                     
29985                     if(box.length){
29986                         boxes.push(box);
29987                         box = [];
29988                     }
29989                     
29990                     boxes.push([item]);
29991                     
29992                     break;
29993                     
29994                 case 'xs' :
29995                 case 'sm' :
29996                 case 'tall' :
29997                     
29998                     box.push(item);
29999                     
30000                     break;
30001                 default :
30002                     break;
30003                     
30004             }
30005             
30006         }, this);
30007         
30008         if(box.length){
30009             boxes.push(box);
30010             box = [];
30011         }
30012         
30013         var filterPattern = function(box, length)
30014         {
30015             if(!box.length){
30016                 return;
30017             }
30018             
30019             var match = false;
30020             
30021             var pattern = box.slice(0, length);
30022             
30023             var format = [];
30024             
30025             Roo.each(pattern, function(i){
30026                 format.push(i.size);
30027             }, this);
30028             
30029             Roo.each(standard, function(s){
30030                 
30031                 if(String(s) != String(format)){
30032                     return;
30033                 }
30034                 
30035                 match = true;
30036                 return false;
30037                 
30038             }, this);
30039             
30040             if(!match && length == 1){
30041                 return;
30042             }
30043             
30044             if(!match){
30045                 filterPattern(box, length - 1);
30046                 return;
30047             }
30048                 
30049             queue.push(pattern);
30050
30051             box = box.slice(length, box.length);
30052
30053             filterPattern(box, 4);
30054
30055             return;
30056             
30057         }
30058         
30059         Roo.each(boxes, function(box, k){
30060             
30061             if(!box.length){
30062                 return;
30063             }
30064             
30065             if(box.length == 1){
30066                 queue.push(box);
30067                 return;
30068             }
30069             
30070             filterPattern(box, 4);
30071             
30072         }, this);
30073         
30074         this._processVerticalLayoutQueue( queue, isInstant );
30075         
30076     },
30077     
30078 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30079 //    {
30080 //        if ( !items || !items.length ) {
30081 //            return;
30082 //        }
30083 //
30084 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30085 //        
30086 //    },
30087     
30088     _horizontalLayoutItems : function ( items , isInstant)
30089     {
30090         if ( !items || !items.length || items.length < 3) {
30091             return;
30092         }
30093         
30094         items.reverse();
30095         
30096         var eItems = items.slice(0, 3);
30097         
30098         items = items.slice(3, items.length);
30099         
30100         var standard = [
30101             ['xs', 'xs', 'xs', 'wide'],
30102             ['xs', 'xs', 'wide'],
30103             ['xs', 'xs', 'sm'],
30104             ['xs', 'xs', 'xs'],
30105             ['xs', 'wide'],
30106             ['xs', 'sm'],
30107             ['xs', 'xs'],
30108             ['xs'],
30109             
30110             ['sm', 'xs', 'xs'],
30111             ['sm', 'xs'],
30112             ['sm'],
30113             
30114             ['wide', 'xs', 'xs', 'xs'],
30115             ['wide', 'xs', 'xs'],
30116             ['wide', 'xs'],
30117             ['wide'],
30118             
30119             ['wide-thin']
30120         ];
30121         
30122         var queue = [];
30123         
30124         var boxes = [];
30125         
30126         var box = [];
30127         
30128         Roo.each(items, function(item, k){
30129             
30130             switch (item.size) {
30131                 case 'md' :
30132                 case 'md-left' :
30133                 case 'md-right' :
30134                 case 'tall' :
30135                     
30136                     if(box.length){
30137                         boxes.push(box);
30138                         box = [];
30139                     }
30140                     
30141                     boxes.push([item]);
30142                     
30143                     break;
30144                     
30145                 case 'xs' :
30146                 case 'sm' :
30147                 case 'wide' :
30148                 case 'wide-thin' :
30149                     
30150                     box.push(item);
30151                     
30152                     break;
30153                 default :
30154                     break;
30155                     
30156             }
30157             
30158         }, this);
30159         
30160         if(box.length){
30161             boxes.push(box);
30162             box = [];
30163         }
30164         
30165         var filterPattern = function(box, length)
30166         {
30167             if(!box.length){
30168                 return;
30169             }
30170             
30171             var match = false;
30172             
30173             var pattern = box.slice(0, length);
30174             
30175             var format = [];
30176             
30177             Roo.each(pattern, function(i){
30178                 format.push(i.size);
30179             }, this);
30180             
30181             Roo.each(standard, function(s){
30182                 
30183                 if(String(s) != String(format)){
30184                     return;
30185                 }
30186                 
30187                 match = true;
30188                 return false;
30189                 
30190             }, this);
30191             
30192             if(!match && length == 1){
30193                 return;
30194             }
30195             
30196             if(!match){
30197                 filterPattern(box, length - 1);
30198                 return;
30199             }
30200                 
30201             queue.push(pattern);
30202
30203             box = box.slice(length, box.length);
30204
30205             filterPattern(box, 4);
30206
30207             return;
30208             
30209         }
30210         
30211         Roo.each(boxes, function(box, k){
30212             
30213             if(!box.length){
30214                 return;
30215             }
30216             
30217             if(box.length == 1){
30218                 queue.push(box);
30219                 return;
30220             }
30221             
30222             filterPattern(box, 4);
30223             
30224         }, this);
30225         
30226         
30227         var prune = [];
30228         
30229         var pos = this.el.getBox(true);
30230         
30231         var minX = pos.x;
30232         
30233         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30234         
30235         var hit_end = false;
30236         
30237         Roo.each(queue, function(box){
30238             
30239             if(hit_end){
30240                 
30241                 Roo.each(box, function(b){
30242                 
30243                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30244                     b.el.hide();
30245
30246                 }, this);
30247
30248                 return;
30249             }
30250             
30251             var mx = 0;
30252             
30253             Roo.each(box, function(b){
30254                 
30255                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30256                 b.el.show();
30257
30258                 mx = Math.max(mx, b.x);
30259                 
30260             }, this);
30261             
30262             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30263             
30264             if(maxX < minX){
30265                 
30266                 Roo.each(box, function(b){
30267                 
30268                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30269                     b.el.hide();
30270                     
30271                 }, this);
30272                 
30273                 hit_end = true;
30274                 
30275                 return;
30276             }
30277             
30278             prune.push(box);
30279             
30280         }, this);
30281         
30282         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30283     },
30284     
30285     /** Sets position of item in DOM
30286     * @param {Element} item
30287     * @param {Number} x - horizontal position
30288     * @param {Number} y - vertical position
30289     * @param {Boolean} isInstant - disables transitions
30290     */
30291     _processVerticalLayoutQueue : function( queue, isInstant )
30292     {
30293         var pos = this.el.getBox(true);
30294         var x = pos.x;
30295         var y = pos.y;
30296         var maxY = [];
30297         
30298         for (var i = 0; i < this.cols; i++){
30299             maxY[i] = pos.y;
30300         }
30301         
30302         Roo.each(queue, function(box, k){
30303             
30304             var col = k % this.cols;
30305             
30306             Roo.each(box, function(b,kk){
30307                 
30308                 b.el.position('absolute');
30309                 
30310                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30311                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30312                 
30313                 if(b.size == 'md-left' || b.size == 'md-right'){
30314                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30315                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30316                 }
30317                 
30318                 b.el.setWidth(width);
30319                 b.el.setHeight(height);
30320                 // iframe?
30321                 b.el.select('iframe',true).setSize(width,height);
30322                 
30323             }, this);
30324             
30325             for (var i = 0; i < this.cols; i++){
30326                 
30327                 if(maxY[i] < maxY[col]){
30328                     col = i;
30329                     continue;
30330                 }
30331                 
30332                 col = Math.min(col, i);
30333                 
30334             }
30335             
30336             x = pos.x + col * (this.colWidth + this.padWidth);
30337             
30338             y = maxY[col];
30339             
30340             var positions = [];
30341             
30342             switch (box.length){
30343                 case 1 :
30344                     positions = this.getVerticalOneBoxColPositions(x, y, box);
30345                     break;
30346                 case 2 :
30347                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
30348                     break;
30349                 case 3 :
30350                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
30351                     break;
30352                 case 4 :
30353                     positions = this.getVerticalFourBoxColPositions(x, y, box);
30354                     break;
30355                 default :
30356                     break;
30357             }
30358             
30359             Roo.each(box, function(b,kk){
30360                 
30361                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30362                 
30363                 var sz = b.el.getSize();
30364                 
30365                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
30366                 
30367             }, this);
30368             
30369         }, this);
30370         
30371         var mY = 0;
30372         
30373         for (var i = 0; i < this.cols; i++){
30374             mY = Math.max(mY, maxY[i]);
30375         }
30376         
30377         this.el.setHeight(mY - pos.y);
30378         
30379     },
30380     
30381 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
30382 //    {
30383 //        var pos = this.el.getBox(true);
30384 //        var x = pos.x;
30385 //        var y = pos.y;
30386 //        var maxX = pos.right;
30387 //        
30388 //        var maxHeight = 0;
30389 //        
30390 //        Roo.each(items, function(item, k){
30391 //            
30392 //            var c = k % 2;
30393 //            
30394 //            item.el.position('absolute');
30395 //                
30396 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30397 //
30398 //            item.el.setWidth(width);
30399 //
30400 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
30401 //
30402 //            item.el.setHeight(height);
30403 //            
30404 //            if(c == 0){
30405 //                item.el.setXY([x, y], isInstant ? false : true);
30406 //            } else {
30407 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
30408 //            }
30409 //            
30410 //            y = y + height + this.alternativePadWidth;
30411 //            
30412 //            maxHeight = maxHeight + height + this.alternativePadWidth;
30413 //            
30414 //        }, this);
30415 //        
30416 //        this.el.setHeight(maxHeight);
30417 //        
30418 //    },
30419     
30420     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30421     {
30422         var pos = this.el.getBox(true);
30423         
30424         var minX = pos.x;
30425         var minY = pos.y;
30426         
30427         var maxX = pos.right;
30428         
30429         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
30430         
30431         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30432         
30433         Roo.each(queue, function(box, k){
30434             
30435             Roo.each(box, function(b, kk){
30436                 
30437                 b.el.position('absolute');
30438                 
30439                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30440                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30441                 
30442                 if(b.size == 'md-left' || b.size == 'md-right'){
30443                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30444                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30445                 }
30446                 
30447                 b.el.setWidth(width);
30448                 b.el.setHeight(height);
30449                 
30450             }, this);
30451             
30452             if(!box.length){
30453                 return;
30454             }
30455             
30456             var positions = [];
30457             
30458             switch (box.length){
30459                 case 1 :
30460                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30461                     break;
30462                 case 2 :
30463                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30464                     break;
30465                 case 3 :
30466                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30467                     break;
30468                 case 4 :
30469                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30470                     break;
30471                 default :
30472                     break;
30473             }
30474             
30475             Roo.each(box, function(b,kk){
30476                 
30477                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30478                 
30479                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30480                 
30481             }, this);
30482             
30483         }, this);
30484         
30485     },
30486     
30487     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30488     {
30489         Roo.each(eItems, function(b,k){
30490             
30491             b.size = (k == 0) ? 'sm' : 'xs';
30492             b.x = (k == 0) ? 2 : 1;
30493             b.y = (k == 0) ? 2 : 1;
30494             
30495             b.el.position('absolute');
30496             
30497             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30498                 
30499             b.el.setWidth(width);
30500             
30501             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30502             
30503             b.el.setHeight(height);
30504             
30505         }, this);
30506
30507         var positions = [];
30508         
30509         positions.push({
30510             x : maxX - this.unitWidth * 2 - this.gutter,
30511             y : minY
30512         });
30513         
30514         positions.push({
30515             x : maxX - this.unitWidth,
30516             y : minY + (this.unitWidth + this.gutter) * 2
30517         });
30518         
30519         positions.push({
30520             x : maxX - this.unitWidth * 3 - this.gutter * 2,
30521             y : minY
30522         });
30523         
30524         Roo.each(eItems, function(b,k){
30525             
30526             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
30527
30528         }, this);
30529         
30530     },
30531     
30532     getVerticalOneBoxColPositions : function(x, y, box)
30533     {
30534         var pos = [];
30535         
30536         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
30537         
30538         if(box[0].size == 'md-left'){
30539             rand = 0;
30540         }
30541         
30542         if(box[0].size == 'md-right'){
30543             rand = 1;
30544         }
30545         
30546         pos.push({
30547             x : x + (this.unitWidth + this.gutter) * rand,
30548             y : y
30549         });
30550         
30551         return pos;
30552     },
30553     
30554     getVerticalTwoBoxColPositions : function(x, y, box)
30555     {
30556         var pos = [];
30557         
30558         if(box[0].size == 'xs'){
30559             
30560             pos.push({
30561                 x : x,
30562                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
30563             });
30564
30565             pos.push({
30566                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
30567                 y : y
30568             });
30569             
30570             return pos;
30571             
30572         }
30573         
30574         pos.push({
30575             x : x,
30576             y : y
30577         });
30578
30579         pos.push({
30580             x : x + (this.unitWidth + this.gutter) * 2,
30581             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
30582         });
30583         
30584         return pos;
30585         
30586     },
30587     
30588     getVerticalThreeBoxColPositions : function(x, y, box)
30589     {
30590         var pos = [];
30591         
30592         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30593             
30594             pos.push({
30595                 x : x,
30596                 y : y
30597             });
30598
30599             pos.push({
30600                 x : x + (this.unitWidth + this.gutter) * 1,
30601                 y : y
30602             });
30603             
30604             pos.push({
30605                 x : x + (this.unitWidth + this.gutter) * 2,
30606                 y : y
30607             });
30608             
30609             return pos;
30610             
30611         }
30612         
30613         if(box[0].size == 'xs' && box[1].size == 'xs'){
30614             
30615             pos.push({
30616                 x : x,
30617                 y : y
30618             });
30619
30620             pos.push({
30621                 x : x,
30622                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
30623             });
30624             
30625             pos.push({
30626                 x : x + (this.unitWidth + this.gutter) * 1,
30627                 y : y
30628             });
30629             
30630             return pos;
30631             
30632         }
30633         
30634         pos.push({
30635             x : x,
30636             y : y
30637         });
30638
30639         pos.push({
30640             x : x + (this.unitWidth + this.gutter) * 2,
30641             y : y
30642         });
30643
30644         pos.push({
30645             x : x + (this.unitWidth + this.gutter) * 2,
30646             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
30647         });
30648             
30649         return pos;
30650         
30651     },
30652     
30653     getVerticalFourBoxColPositions : function(x, y, box)
30654     {
30655         var pos = [];
30656         
30657         if(box[0].size == 'xs'){
30658             
30659             pos.push({
30660                 x : x,
30661                 y : y
30662             });
30663
30664             pos.push({
30665                 x : x,
30666                 y : y + (this.unitHeight + this.gutter) * 1
30667             });
30668             
30669             pos.push({
30670                 x : x,
30671                 y : y + (this.unitHeight + this.gutter) * 2
30672             });
30673             
30674             pos.push({
30675                 x : x + (this.unitWidth + this.gutter) * 1,
30676                 y : y
30677             });
30678             
30679             return pos;
30680             
30681         }
30682         
30683         pos.push({
30684             x : x,
30685             y : y
30686         });
30687
30688         pos.push({
30689             x : x + (this.unitWidth + this.gutter) * 2,
30690             y : y
30691         });
30692
30693         pos.push({
30694             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
30695             y : y + (this.unitHeight + this.gutter) * 1
30696         });
30697
30698         pos.push({
30699             x : x + (this.unitWidth + this.gutter) * 2,
30700             y : y + (this.unitWidth + this.gutter) * 2
30701         });
30702
30703         return pos;
30704         
30705     },
30706     
30707     getHorizontalOneBoxColPositions : function(maxX, minY, box)
30708     {
30709         var pos = [];
30710         
30711         if(box[0].size == 'md-left'){
30712             pos.push({
30713                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30714                 y : minY
30715             });
30716             
30717             return pos;
30718         }
30719         
30720         if(box[0].size == 'md-right'){
30721             pos.push({
30722                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30723                 y : minY + (this.unitWidth + this.gutter) * 1
30724             });
30725             
30726             return pos;
30727         }
30728         
30729         var rand = Math.floor(Math.random() * (4 - box[0].y));
30730         
30731         pos.push({
30732             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30733             y : minY + (this.unitWidth + this.gutter) * rand
30734         });
30735         
30736         return pos;
30737         
30738     },
30739     
30740     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
30741     {
30742         var pos = [];
30743         
30744         if(box[0].size == 'xs'){
30745             
30746             pos.push({
30747                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30748                 y : minY
30749             });
30750
30751             pos.push({
30752                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30753                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
30754             });
30755             
30756             return pos;
30757             
30758         }
30759         
30760         pos.push({
30761             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30762             y : minY
30763         });
30764
30765         pos.push({
30766             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30767             y : minY + (this.unitWidth + this.gutter) * 2
30768         });
30769         
30770         return pos;
30771         
30772     },
30773     
30774     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30775     {
30776         var pos = [];
30777         
30778         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30779             
30780             pos.push({
30781                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30782                 y : minY
30783             });
30784
30785             pos.push({
30786                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30787                 y : minY + (this.unitWidth + this.gutter) * 1
30788             });
30789             
30790             pos.push({
30791                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30792                 y : minY + (this.unitWidth + this.gutter) * 2
30793             });
30794             
30795             return pos;
30796             
30797         }
30798         
30799         if(box[0].size == 'xs' && box[1].size == 'xs'){
30800             
30801             pos.push({
30802                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30803                 y : minY
30804             });
30805
30806             pos.push({
30807                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30808                 y : minY
30809             });
30810             
30811             pos.push({
30812                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30813                 y : minY + (this.unitWidth + this.gutter) * 1
30814             });
30815             
30816             return pos;
30817             
30818         }
30819         
30820         pos.push({
30821             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30822             y : minY
30823         });
30824
30825         pos.push({
30826             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30827             y : minY + (this.unitWidth + this.gutter) * 2
30828         });
30829
30830         pos.push({
30831             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30832             y : minY + (this.unitWidth + this.gutter) * 2
30833         });
30834             
30835         return pos;
30836         
30837     },
30838     
30839     getHorizontalFourBoxColPositions : function(maxX, minY, box)
30840     {
30841         var pos = [];
30842         
30843         if(box[0].size == 'xs'){
30844             
30845             pos.push({
30846                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30847                 y : minY
30848             });
30849
30850             pos.push({
30851                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30852                 y : minY
30853             });
30854             
30855             pos.push({
30856                 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),
30857                 y : minY
30858             });
30859             
30860             pos.push({
30861                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30862                 y : minY + (this.unitWidth + this.gutter) * 1
30863             });
30864             
30865             return pos;
30866             
30867         }
30868         
30869         pos.push({
30870             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30871             y : minY
30872         });
30873         
30874         pos.push({
30875             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30876             y : minY + (this.unitWidth + this.gutter) * 2
30877         });
30878         
30879         pos.push({
30880             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30881             y : minY + (this.unitWidth + this.gutter) * 2
30882         });
30883         
30884         pos.push({
30885             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),
30886             y : minY + (this.unitWidth + this.gutter) * 2
30887         });
30888
30889         return pos;
30890         
30891     }
30892     
30893 });
30894
30895  
30896
30897  /**
30898  *
30899  * This is based on 
30900  * http://masonry.desandro.com
30901  *
30902  * The idea is to render all the bricks based on vertical width...
30903  *
30904  * The original code extends 'outlayer' - we might need to use that....
30905  * 
30906  */
30907
30908
30909 /**
30910  * @class Roo.bootstrap.LayoutMasonryAuto
30911  * @extends Roo.bootstrap.Component
30912  * Bootstrap Layout Masonry class
30913  * 
30914  * @constructor
30915  * Create a new Element
30916  * @param {Object} config The config object
30917  */
30918
30919 Roo.bootstrap.LayoutMasonryAuto = function(config){
30920     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30921 };
30922
30923 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
30924     
30925       /**
30926      * @cfg {Boolean} isFitWidth  - resize the width..
30927      */   
30928     isFitWidth : false,  // options..
30929     /**
30930      * @cfg {Boolean} isOriginLeft = left align?
30931      */   
30932     isOriginLeft : true,
30933     /**
30934      * @cfg {Boolean} isOriginTop = top align?
30935      */   
30936     isOriginTop : false,
30937     /**
30938      * @cfg {Boolean} isLayoutInstant = no animation?
30939      */   
30940     isLayoutInstant : false, // needed?
30941     /**
30942      * @cfg {Boolean} isResizingContainer = not sure if this is used..
30943      */   
30944     isResizingContainer : true,
30945     /**
30946      * @cfg {Number} columnWidth  width of the columns 
30947      */   
30948     
30949     columnWidth : 0,
30950     
30951     /**
30952      * @cfg {Number} maxCols maximum number of columns
30953      */   
30954     
30955     maxCols: 0,
30956     /**
30957      * @cfg {Number} padHeight padding below box..
30958      */   
30959     
30960     padHeight : 10, 
30961     
30962     /**
30963      * @cfg {Boolean} isAutoInitial defalut true
30964      */   
30965     
30966     isAutoInitial : true, 
30967     
30968     // private?
30969     gutter : 0,
30970     
30971     containerWidth: 0,
30972     initialColumnWidth : 0,
30973     currentSize : null,
30974     
30975     colYs : null, // array.
30976     maxY : 0,
30977     padWidth: 10,
30978     
30979     
30980     tag: 'div',
30981     cls: '',
30982     bricks: null, //CompositeElement
30983     cols : 0, // array?
30984     // element : null, // wrapped now this.el
30985     _isLayoutInited : null, 
30986     
30987     
30988     getAutoCreate : function(){
30989         
30990         var cfg = {
30991             tag: this.tag,
30992             cls: 'blog-masonary-wrapper ' + this.cls,
30993             cn : {
30994                 cls : 'mas-boxes masonary'
30995             }
30996         };
30997         
30998         return cfg;
30999     },
31000     
31001     getChildContainer: function( )
31002     {
31003         if (this.boxesEl) {
31004             return this.boxesEl;
31005         }
31006         
31007         this.boxesEl = this.el.select('.mas-boxes').first();
31008         
31009         return this.boxesEl;
31010     },
31011     
31012     
31013     initEvents : function()
31014     {
31015         var _this = this;
31016         
31017         if(this.isAutoInitial){
31018             Roo.log('hook children rendered');
31019             this.on('childrenrendered', function() {
31020                 Roo.log('children rendered');
31021                 _this.initial();
31022             } ,this);
31023         }
31024         
31025     },
31026     
31027     initial : function()
31028     {
31029         this.reloadItems();
31030
31031         this.currentSize = this.el.getBox(true);
31032
31033         /// was window resize... - let's see if this works..
31034         Roo.EventManager.onWindowResize(this.resize, this); 
31035
31036         if(!this.isAutoInitial){
31037             this.layout();
31038             return;
31039         }
31040         
31041         this.layout.defer(500,this);
31042     },
31043     
31044     reloadItems: function()
31045     {
31046         this.bricks = this.el.select('.masonry-brick', true);
31047         
31048         this.bricks.each(function(b) {
31049             //Roo.log(b.getSize());
31050             if (!b.attr('originalwidth')) {
31051                 b.attr('originalwidth',  b.getSize().width);
31052             }
31053             
31054         });
31055         
31056         Roo.log(this.bricks.elements.length);
31057     },
31058     
31059     resize : function()
31060     {
31061         Roo.log('resize');
31062         var cs = this.el.getBox(true);
31063         
31064         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31065             Roo.log("no change in with or X");
31066             return;
31067         }
31068         this.currentSize = cs;
31069         this.layout();
31070     },
31071     
31072     layout : function()
31073     {
31074          Roo.log('layout');
31075         this._resetLayout();
31076         //this._manageStamps();
31077       
31078         // don't animate first layout
31079         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31080         this.layoutItems( isInstant );
31081       
31082         // flag for initalized
31083         this._isLayoutInited = true;
31084     },
31085     
31086     layoutItems : function( isInstant )
31087     {
31088         //var items = this._getItemsForLayout( this.items );
31089         // original code supports filtering layout items.. we just ignore it..
31090         
31091         this._layoutItems( this.bricks , isInstant );
31092       
31093         this._postLayout();
31094     },
31095     _layoutItems : function ( items , isInstant)
31096     {
31097        //this.fireEvent( 'layout', this, items );
31098     
31099
31100         if ( !items || !items.elements.length ) {
31101           // no items, emit event with empty array
31102             return;
31103         }
31104
31105         var queue = [];
31106         items.each(function(item) {
31107             Roo.log("layout item");
31108             Roo.log(item);
31109             // get x/y object from method
31110             var position = this._getItemLayoutPosition( item );
31111             // enqueue
31112             position.item = item;
31113             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31114             queue.push( position );
31115         }, this);
31116       
31117         this._processLayoutQueue( queue );
31118     },
31119     /** Sets position of item in DOM
31120     * @param {Element} item
31121     * @param {Number} x - horizontal position
31122     * @param {Number} y - vertical position
31123     * @param {Boolean} isInstant - disables transitions
31124     */
31125     _processLayoutQueue : function( queue )
31126     {
31127         for ( var i=0, len = queue.length; i < len; i++ ) {
31128             var obj = queue[i];
31129             obj.item.position('absolute');
31130             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31131         }
31132     },
31133       
31134     
31135     /**
31136     * Any logic you want to do after each layout,
31137     * i.e. size the container
31138     */
31139     _postLayout : function()
31140     {
31141         this.resizeContainer();
31142     },
31143     
31144     resizeContainer : function()
31145     {
31146         if ( !this.isResizingContainer ) {
31147             return;
31148         }
31149         var size = this._getContainerSize();
31150         if ( size ) {
31151             this.el.setSize(size.width,size.height);
31152             this.boxesEl.setSize(size.width,size.height);
31153         }
31154     },
31155     
31156     
31157     
31158     _resetLayout : function()
31159     {
31160         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31161         this.colWidth = this.el.getWidth();
31162         //this.gutter = this.el.getWidth(); 
31163         
31164         this.measureColumns();
31165
31166         // reset column Y
31167         var i = this.cols;
31168         this.colYs = [];
31169         while (i--) {
31170             this.colYs.push( 0 );
31171         }
31172     
31173         this.maxY = 0;
31174     },
31175
31176     measureColumns : function()
31177     {
31178         this.getContainerWidth();
31179       // if columnWidth is 0, default to outerWidth of first item
31180         if ( !this.columnWidth ) {
31181             var firstItem = this.bricks.first();
31182             Roo.log(firstItem);
31183             this.columnWidth  = this.containerWidth;
31184             if (firstItem && firstItem.attr('originalwidth') ) {
31185                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31186             }
31187             // columnWidth fall back to item of first element
31188             Roo.log("set column width?");
31189                         this.initialColumnWidth = this.columnWidth  ;
31190
31191             // if first elem has no width, default to size of container
31192             
31193         }
31194         
31195         
31196         if (this.initialColumnWidth) {
31197             this.columnWidth = this.initialColumnWidth;
31198         }
31199         
31200         
31201             
31202         // column width is fixed at the top - however if container width get's smaller we should
31203         // reduce it...
31204         
31205         // this bit calcs how man columns..
31206             
31207         var columnWidth = this.columnWidth += this.gutter;
31208       
31209         // calculate columns
31210         var containerWidth = this.containerWidth + this.gutter;
31211         
31212         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31213         // fix rounding errors, typically with gutters
31214         var excess = columnWidth - containerWidth % columnWidth;
31215         
31216         
31217         // if overshoot is less than a pixel, round up, otherwise floor it
31218         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31219         cols = Math[ mathMethod ]( cols );
31220         this.cols = Math.max( cols, 1 );
31221         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31222         
31223          // padding positioning..
31224         var totalColWidth = this.cols * this.columnWidth;
31225         var padavail = this.containerWidth - totalColWidth;
31226         // so for 2 columns - we need 3 'pads'
31227         
31228         var padNeeded = (1+this.cols) * this.padWidth;
31229         
31230         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
31231         
31232         this.columnWidth += padExtra
31233         //this.padWidth = Math.floor(padavail /  ( this.cols));
31234         
31235         // adjust colum width so that padding is fixed??
31236         
31237         // we have 3 columns ... total = width * 3
31238         // we have X left over... that should be used by 
31239         
31240         //if (this.expandC) {
31241             
31242         //}
31243         
31244         
31245         
31246     },
31247     
31248     getContainerWidth : function()
31249     {
31250        /* // container is parent if fit width
31251         var container = this.isFitWidth ? this.element.parentNode : this.element;
31252         // check that this.size and size are there
31253         // IE8 triggers resize on body size change, so they might not be
31254         
31255         var size = getSize( container );  //FIXME
31256         this.containerWidth = size && size.innerWidth; //FIXME
31257         */
31258          
31259         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31260         
31261     },
31262     
31263     _getItemLayoutPosition : function( item )  // what is item?
31264     {
31265         // we resize the item to our columnWidth..
31266       
31267         item.setWidth(this.columnWidth);
31268         item.autoBoxAdjust  = false;
31269         
31270         var sz = item.getSize();
31271  
31272         // how many columns does this brick span
31273         var remainder = this.containerWidth % this.columnWidth;
31274         
31275         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
31276         // round if off by 1 pixel, otherwise use ceil
31277         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
31278         colSpan = Math.min( colSpan, this.cols );
31279         
31280         // normally this should be '1' as we dont' currently allow multi width columns..
31281         
31282         var colGroup = this._getColGroup( colSpan );
31283         // get the minimum Y value from the columns
31284         var minimumY = Math.min.apply( Math, colGroup );
31285         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31286         
31287         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
31288          
31289         // position the brick
31290         var position = {
31291             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
31292             y: this.currentSize.y + minimumY + this.padHeight
31293         };
31294         
31295         Roo.log(position);
31296         // apply setHeight to necessary columns
31297         var setHeight = minimumY + sz.height + this.padHeight;
31298         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31299         
31300         var setSpan = this.cols + 1 - colGroup.length;
31301         for ( var i = 0; i < setSpan; i++ ) {
31302           this.colYs[ shortColIndex + i ] = setHeight ;
31303         }
31304       
31305         return position;
31306     },
31307     
31308     /**
31309      * @param {Number} colSpan - number of columns the element spans
31310      * @returns {Array} colGroup
31311      */
31312     _getColGroup : function( colSpan )
31313     {
31314         if ( colSpan < 2 ) {
31315           // if brick spans only one column, use all the column Ys
31316           return this.colYs;
31317         }
31318       
31319         var colGroup = [];
31320         // how many different places could this brick fit horizontally
31321         var groupCount = this.cols + 1 - colSpan;
31322         // for each group potential horizontal position
31323         for ( var i = 0; i < groupCount; i++ ) {
31324           // make an array of colY values for that one group
31325           var groupColYs = this.colYs.slice( i, i + colSpan );
31326           // and get the max value of the array
31327           colGroup[i] = Math.max.apply( Math, groupColYs );
31328         }
31329         return colGroup;
31330     },
31331     /*
31332     _manageStamp : function( stamp )
31333     {
31334         var stampSize =  stamp.getSize();
31335         var offset = stamp.getBox();
31336         // get the columns that this stamp affects
31337         var firstX = this.isOriginLeft ? offset.x : offset.right;
31338         var lastX = firstX + stampSize.width;
31339         var firstCol = Math.floor( firstX / this.columnWidth );
31340         firstCol = Math.max( 0, firstCol );
31341         
31342         var lastCol = Math.floor( lastX / this.columnWidth );
31343         // lastCol should not go over if multiple of columnWidth #425
31344         lastCol -= lastX % this.columnWidth ? 0 : 1;
31345         lastCol = Math.min( this.cols - 1, lastCol );
31346         
31347         // set colYs to bottom of the stamp
31348         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
31349             stampSize.height;
31350             
31351         for ( var i = firstCol; i <= lastCol; i++ ) {
31352           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
31353         }
31354     },
31355     */
31356     
31357     _getContainerSize : function()
31358     {
31359         this.maxY = Math.max.apply( Math, this.colYs );
31360         var size = {
31361             height: this.maxY
31362         };
31363       
31364         if ( this.isFitWidth ) {
31365             size.width = this._getContainerFitWidth();
31366         }
31367       
31368         return size;
31369     },
31370     
31371     _getContainerFitWidth : function()
31372     {
31373         var unusedCols = 0;
31374         // count unused columns
31375         var i = this.cols;
31376         while ( --i ) {
31377           if ( this.colYs[i] !== 0 ) {
31378             break;
31379           }
31380           unusedCols++;
31381         }
31382         // fit container to columns that have been used
31383         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
31384     },
31385     
31386     needsResizeLayout : function()
31387     {
31388         var previousWidth = this.containerWidth;
31389         this.getContainerWidth();
31390         return previousWidth !== this.containerWidth;
31391     }
31392  
31393 });
31394
31395  
31396
31397  /*
31398  * - LGPL
31399  *
31400  * element
31401  * 
31402  */
31403
31404 /**
31405  * @class Roo.bootstrap.MasonryBrick
31406  * @extends Roo.bootstrap.Component
31407  * Bootstrap MasonryBrick class
31408  * 
31409  * @constructor
31410  * Create a new MasonryBrick
31411  * @param {Object} config The config object
31412  */
31413
31414 Roo.bootstrap.MasonryBrick = function(config){
31415     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
31416     
31417     this.addEvents({
31418         // raw events
31419         /**
31420          * @event click
31421          * When a MasonryBrick is clcik
31422          * @param {Roo.bootstrap.MasonryBrick} this
31423          * @param {Roo.EventObject} e
31424          */
31425         "click" : true
31426     });
31427 };
31428
31429 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
31430     
31431     /**
31432      * @cfg {String} title
31433      */   
31434     title : '',
31435     /**
31436      * @cfg {String} html
31437      */   
31438     html : '',
31439     /**
31440      * @cfg {String} bgimage
31441      */   
31442     bgimage : '',
31443     /**
31444      * @cfg {String} videourl
31445      */   
31446     videourl : '',
31447     /**
31448      * @cfg {String} cls
31449      */   
31450     cls : '',
31451     /**
31452      * @cfg {String} href
31453      */   
31454     href : '',
31455     /**
31456      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
31457      */   
31458     size : 'xs',
31459     
31460     /**
31461      * @cfg {String} placetitle (center|bottom)
31462      */   
31463     placetitle : '',
31464     
31465     /**
31466      * @cfg {Boolean} isFitContainer defalut true
31467      */   
31468     isFitContainer : true, 
31469     
31470     /**
31471      * @cfg {Boolean} preventDefault defalut false
31472      */   
31473     preventDefault : false, 
31474     
31475     /**
31476      * @cfg {Boolean} inverse defalut false
31477      */   
31478     maskInverse : false, 
31479     
31480     getAutoCreate : function()
31481     {
31482         if(!this.isFitContainer){
31483             return this.getSplitAutoCreate();
31484         }
31485         
31486         var cls = 'masonry-brick masonry-brick-full';
31487         
31488         if(this.href.length){
31489             cls += ' masonry-brick-link';
31490         }
31491         
31492         if(this.bgimage.length){
31493             cls += ' masonry-brick-image';
31494         }
31495         
31496         if(this.maskInverse){
31497             cls += ' mask-inverse';
31498         }
31499         
31500         if(!this.html.length && !this.maskInverse){
31501             cls += ' enable-mask';
31502         }
31503         
31504         if(this.size){
31505             cls += ' masonry-' + this.size + '-brick';
31506         }
31507         
31508         if(this.placetitle.length){
31509             
31510             switch (this.placetitle) {
31511                 case 'center' :
31512                     cls += ' masonry-center-title';
31513                     break;
31514                 case 'bottom' :
31515                     cls += ' masonry-bottom-title';
31516                     break;
31517                 default:
31518                     break;
31519             }
31520             
31521         } else {
31522             if(!this.html.length && !this.bgimage.length){
31523                 cls += ' masonry-center-title';
31524             }
31525
31526             if(!this.html.length && this.bgimage.length){
31527                 cls += ' masonry-bottom-title';
31528             }
31529         }
31530         
31531         if(this.cls){
31532             cls += ' ' + this.cls;
31533         }
31534         
31535         var cfg = {
31536             tag: (this.href.length) ? 'a' : 'div',
31537             cls: cls,
31538             cn: [
31539                 {
31540                     tag: 'div',
31541                     cls: 'masonry-brick-paragraph',
31542                     cn: []
31543                 }
31544             ]
31545         };
31546         
31547         if(this.href.length){
31548             cfg.href = this.href;
31549         }
31550         
31551         var cn = cfg.cn[0].cn;
31552         
31553         if(this.title.length){
31554             cn.push({
31555                 tag: 'h4',
31556                 cls: 'masonry-brick-title',
31557                 html: this.title
31558             });
31559         }
31560         
31561         if(this.html.length){
31562             cn.push({
31563                 tag: 'p',
31564                 cls: 'masonry-brick-text',
31565                 html: this.html
31566             });
31567         }  
31568         if (!this.title.length && !this.html.length) {
31569             cfg.cn[0].cls += ' hide';
31570         }
31571         
31572         if(this.bgimage.length){
31573             cfg.cn.push({
31574                 tag: 'img',
31575                 cls: 'masonry-brick-image-view',
31576                 src: this.bgimage
31577             });
31578         }
31579         
31580         if(this.videourl.length){
31581             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31582             // youtube support only?
31583             cfg.cn.push({
31584                 tag: 'iframe',
31585                 cls: 'masonry-brick-image-view',
31586                 src: vurl,
31587                 frameborder : 0,
31588                 allowfullscreen : true
31589             });
31590             
31591             
31592         }
31593         
31594         cfg.cn.push({
31595             tag: 'div',
31596             cls: 'masonry-brick-mask'
31597         });
31598         
31599         return cfg;
31600         
31601     },
31602     
31603     getSplitAutoCreate : function()
31604     {
31605         var cls = 'masonry-brick masonry-brick-split';
31606         
31607         if(this.href.length){
31608             cls += ' masonry-brick-link';
31609         }
31610         
31611         if(this.bgimage.length){
31612             cls += ' masonry-brick-image';
31613         }
31614         
31615         if(this.size){
31616             cls += ' masonry-' + this.size + '-brick';
31617         }
31618         
31619         switch (this.placetitle) {
31620             case 'center' :
31621                 cls += ' masonry-center-title';
31622                 break;
31623             case 'bottom' :
31624                 cls += ' masonry-bottom-title';
31625                 break;
31626             default:
31627                 if(!this.bgimage.length){
31628                     cls += ' masonry-center-title';
31629                 }
31630
31631                 if(this.bgimage.length){
31632                     cls += ' masonry-bottom-title';
31633                 }
31634                 break;
31635         }
31636         
31637         if(this.cls){
31638             cls += ' ' + this.cls;
31639         }
31640         
31641         var cfg = {
31642             tag: (this.href.length) ? 'a' : 'div',
31643             cls: cls,
31644             cn: [
31645                 {
31646                     tag: 'div',
31647                     cls: 'masonry-brick-split-head',
31648                     cn: [
31649                         {
31650                             tag: 'div',
31651                             cls: 'masonry-brick-paragraph',
31652                             cn: []
31653                         }
31654                     ]
31655                 },
31656                 {
31657                     tag: 'div',
31658                     cls: 'masonry-brick-split-body',
31659                     cn: []
31660                 }
31661             ]
31662         };
31663         
31664         if(this.href.length){
31665             cfg.href = this.href;
31666         }
31667         
31668         if(this.title.length){
31669             cfg.cn[0].cn[0].cn.push({
31670                 tag: 'h4',
31671                 cls: 'masonry-brick-title',
31672                 html: this.title
31673             });
31674         }
31675         
31676         if(this.html.length){
31677             cfg.cn[1].cn.push({
31678                 tag: 'p',
31679                 cls: 'masonry-brick-text',
31680                 html: this.html
31681             });
31682         }
31683
31684         if(this.bgimage.length){
31685             cfg.cn[0].cn.push({
31686                 tag: 'img',
31687                 cls: 'masonry-brick-image-view',
31688                 src: this.bgimage
31689             });
31690         }
31691         
31692         if(this.videourl.length){
31693             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31694             // youtube support only?
31695             cfg.cn[0].cn.cn.push({
31696                 tag: 'iframe',
31697                 cls: 'masonry-brick-image-view',
31698                 src: vurl,
31699                 frameborder : 0,
31700                 allowfullscreen : true
31701             });
31702         }
31703         
31704         return cfg;
31705     },
31706     
31707     initEvents: function() 
31708     {
31709         switch (this.size) {
31710             case 'xs' :
31711                 this.x = 1;
31712                 this.y = 1;
31713                 break;
31714             case 'sm' :
31715                 this.x = 2;
31716                 this.y = 2;
31717                 break;
31718             case 'md' :
31719             case 'md-left' :
31720             case 'md-right' :
31721                 this.x = 3;
31722                 this.y = 3;
31723                 break;
31724             case 'tall' :
31725                 this.x = 2;
31726                 this.y = 3;
31727                 break;
31728             case 'wide' :
31729                 this.x = 3;
31730                 this.y = 2;
31731                 break;
31732             case 'wide-thin' :
31733                 this.x = 3;
31734                 this.y = 1;
31735                 break;
31736                         
31737             default :
31738                 break;
31739         }
31740         
31741         if(Roo.isTouch){
31742             this.el.on('touchstart', this.onTouchStart, this);
31743             this.el.on('touchmove', this.onTouchMove, this);
31744             this.el.on('touchend', this.onTouchEnd, this);
31745             this.el.on('contextmenu', this.onContextMenu, this);
31746         } else {
31747             this.el.on('mouseenter'  ,this.enter, this);
31748             this.el.on('mouseleave', this.leave, this);
31749             this.el.on('click', this.onClick, this);
31750         }
31751         
31752         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
31753             this.parent().bricks.push(this);   
31754         }
31755         
31756     },
31757     
31758     onClick: function(e, el)
31759     {
31760         var time = this.endTimer - this.startTimer;
31761         
31762         if(Roo.isTouch){
31763             if(time > 1000){
31764                 e.preventDefault();
31765                 return;
31766             }
31767         }
31768         
31769         if(!this.preventDefault){
31770             return;
31771         }
31772         
31773         e.preventDefault();
31774         this.fireEvent('click', this);
31775     },
31776     
31777     enter: function(e, el)
31778     {
31779         e.preventDefault();
31780         
31781         if(!this.isFitContainer || this.maskInverse){
31782             return;
31783         }
31784         
31785         if(this.bgimage.length && this.html.length){
31786             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31787         }
31788     },
31789     
31790     leave: function(e, el)
31791     {
31792         e.preventDefault();
31793         
31794         if(!this.isFitContainer || this.maskInverse){
31795             return;
31796         }
31797         
31798         if(this.bgimage.length && this.html.length){
31799             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31800         }
31801     },
31802     
31803     onTouchStart: function(e, el)
31804     {
31805 //        e.preventDefault();
31806         
31807         this.touchmoved = false;
31808         
31809         if(!this.isFitContainer){
31810             return;
31811         }
31812         
31813         if(!this.bgimage.length || !this.html.length){
31814             return;
31815         }
31816         
31817         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31818         
31819         this.timer = new Date().getTime();
31820         
31821     },
31822     
31823     onTouchMove: function(e, el)
31824     {
31825         this.touchmoved = true;
31826     },
31827     
31828     onContextMenu : function(e,el)
31829     {
31830         e.preventDefault();
31831         e.stopPropagation();
31832         return false;
31833     },
31834     
31835     onTouchEnd: function(e, el)
31836     {
31837 //        e.preventDefault();
31838         
31839         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
31840         
31841             this.leave(e,el);
31842             
31843             return;
31844         }
31845         
31846         if(!this.bgimage.length || !this.html.length){
31847             
31848             if(this.href.length){
31849                 window.location.href = this.href;
31850             }
31851             
31852             return;
31853         }
31854         
31855         if(!this.isFitContainer){
31856             return;
31857         }
31858         
31859         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31860         
31861         window.location.href = this.href;
31862     }
31863     
31864 });
31865
31866  
31867
31868  /*
31869  * - LGPL
31870  *
31871  * element
31872  * 
31873  */
31874
31875 /**
31876  * @class Roo.bootstrap.Brick
31877  * @extends Roo.bootstrap.Component
31878  * Bootstrap Brick class
31879  * 
31880  * @constructor
31881  * Create a new Brick
31882  * @param {Object} config The config object
31883  */
31884
31885 Roo.bootstrap.Brick = function(config){
31886     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
31887     
31888     this.addEvents({
31889         // raw events
31890         /**
31891          * @event click
31892          * When a Brick is click
31893          * @param {Roo.bootstrap.Brick} this
31894          * @param {Roo.EventObject} e
31895          */
31896         "click" : true
31897     });
31898 };
31899
31900 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
31901     
31902     /**
31903      * @cfg {String} title
31904      */   
31905     title : '',
31906     /**
31907      * @cfg {String} html
31908      */   
31909     html : '',
31910     /**
31911      * @cfg {String} bgimage
31912      */   
31913     bgimage : '',
31914     /**
31915      * @cfg {String} cls
31916      */   
31917     cls : '',
31918     /**
31919      * @cfg {String} href
31920      */   
31921     href : '',
31922     /**
31923      * @cfg {String} video
31924      */   
31925     video : '',
31926     /**
31927      * @cfg {Boolean} square
31928      */   
31929     square : true,
31930     
31931     getAutoCreate : function()
31932     {
31933         var cls = 'roo-brick';
31934         
31935         if(this.href.length){
31936             cls += ' roo-brick-link';
31937         }
31938         
31939         if(this.bgimage.length){
31940             cls += ' roo-brick-image';
31941         }
31942         
31943         if(!this.html.length && !this.bgimage.length){
31944             cls += ' roo-brick-center-title';
31945         }
31946         
31947         if(!this.html.length && this.bgimage.length){
31948             cls += ' roo-brick-bottom-title';
31949         }
31950         
31951         if(this.cls){
31952             cls += ' ' + this.cls;
31953         }
31954         
31955         var cfg = {
31956             tag: (this.href.length) ? 'a' : 'div',
31957             cls: cls,
31958             cn: [
31959                 {
31960                     tag: 'div',
31961                     cls: 'roo-brick-paragraph',
31962                     cn: []
31963                 }
31964             ]
31965         };
31966         
31967         if(this.href.length){
31968             cfg.href = this.href;
31969         }
31970         
31971         var cn = cfg.cn[0].cn;
31972         
31973         if(this.title.length){
31974             cn.push({
31975                 tag: 'h4',
31976                 cls: 'roo-brick-title',
31977                 html: this.title
31978             });
31979         }
31980         
31981         if(this.html.length){
31982             cn.push({
31983                 tag: 'p',
31984                 cls: 'roo-brick-text',
31985                 html: this.html
31986             });
31987         } else {
31988             cn.cls += ' hide';
31989         }
31990         
31991         if(this.bgimage.length){
31992             cfg.cn.push({
31993                 tag: 'img',
31994                 cls: 'roo-brick-image-view',
31995                 src: this.bgimage
31996             });
31997         }
31998         
31999         return cfg;
32000     },
32001     
32002     initEvents: function() 
32003     {
32004         if(this.title.length || this.html.length){
32005             this.el.on('mouseenter'  ,this.enter, this);
32006             this.el.on('mouseleave', this.leave, this);
32007         }
32008         
32009         
32010         Roo.EventManager.onWindowResize(this.resize, this); 
32011         
32012         this.resize();
32013     },
32014     
32015     resize : function()
32016     {
32017         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32018         
32019         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32020         
32021         if(this.bgimage.length){
32022             var image = this.el.select('.roo-brick-image-view', true).first();
32023             image.setWidth(paragraph.getWidth());
32024             image.setHeight(paragraph.getWidth());
32025             
32026             this.el.setHeight(paragraph.getWidth());
32027             
32028         }
32029         
32030     },
32031     
32032     enter: function(e, el)
32033     {
32034         e.preventDefault();
32035         
32036         if(this.bgimage.length){
32037             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32038             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32039         }
32040     },
32041     
32042     leave: function(e, el)
32043     {
32044         e.preventDefault();
32045         
32046         if(this.bgimage.length){
32047             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32048             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32049         }
32050     }
32051     
32052 });
32053
32054  
32055
32056  /*
32057  * - LGPL
32058  *
32059  * Input
32060  * 
32061  */
32062
32063 /**
32064  * @class Roo.bootstrap.NumberField
32065  * @extends Roo.bootstrap.Input
32066  * Bootstrap NumberField class
32067  * 
32068  * 
32069  * 
32070  * 
32071  * @constructor
32072  * Create a new NumberField
32073  * @param {Object} config The config object
32074  */
32075
32076 Roo.bootstrap.NumberField = function(config){
32077     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32078 };
32079
32080 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32081     
32082     /**
32083      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32084      */
32085     allowDecimals : true,
32086     /**
32087      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32088      */
32089     decimalSeparator : ".",
32090     /**
32091      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32092      */
32093     decimalPrecision : 2,
32094     /**
32095      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32096      */
32097     allowNegative : true,
32098     /**
32099      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32100      */
32101     minValue : Number.NEGATIVE_INFINITY,
32102     /**
32103      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32104      */
32105     maxValue : Number.MAX_VALUE,
32106     /**
32107      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32108      */
32109     minText : "The minimum value for this field is {0}",
32110     /**
32111      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32112      */
32113     maxText : "The maximum value for this field is {0}",
32114     /**
32115      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
32116      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32117      */
32118     nanText : "{0} is not a valid number",
32119     /**
32120      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32121      */
32122     castInt : true,
32123
32124     // private
32125     initEvents : function()
32126     {   
32127         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32128         
32129         var allowed = "0123456789";
32130         
32131         if(this.allowDecimals){
32132             allowed += this.decimalSeparator;
32133         }
32134         
32135         if(this.allowNegative){
32136             allowed += "-";
32137         }
32138         
32139         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32140         
32141         var keyPress = function(e){
32142             
32143             var k = e.getKey();
32144             
32145             var c = e.getCharCode();
32146             
32147             if(
32148                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
32149                     allowed.indexOf(String.fromCharCode(c)) === -1
32150             ){
32151                 e.stopEvent();
32152                 return;
32153             }
32154             
32155             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
32156                 return;
32157             }
32158             
32159             if(allowed.indexOf(String.fromCharCode(c)) === -1){
32160                 e.stopEvent();
32161             }
32162         };
32163         
32164         this.el.on("keypress", keyPress, this);
32165     },
32166     
32167     validateValue : function(value)
32168     {
32169         
32170         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
32171             return false;
32172         }
32173         
32174         var num = this.parseValue(value);
32175         
32176         if(isNaN(num)){
32177             this.markInvalid(String.format(this.nanText, value));
32178             return false;
32179         }
32180         
32181         if(num < this.minValue){
32182             this.markInvalid(String.format(this.minText, this.minValue));
32183             return false;
32184         }
32185         
32186         if(num > this.maxValue){
32187             this.markInvalid(String.format(this.maxText, this.maxValue));
32188             return false;
32189         }
32190         
32191         return true;
32192     },
32193
32194     getValue : function()
32195     {
32196         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
32197     },
32198
32199     parseValue : function(value)
32200     {
32201         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
32202         return isNaN(value) ? '' : value;
32203     },
32204
32205     fixPrecision : function(value)
32206     {
32207         var nan = isNaN(value);
32208         
32209         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
32210             return nan ? '' : value;
32211         }
32212         return parseFloat(value).toFixed(this.decimalPrecision);
32213     },
32214
32215     setValue : function(v)
32216     {
32217         v = this.fixPrecision(v);
32218         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
32219     },
32220
32221     decimalPrecisionFcn : function(v)
32222     {
32223         return Math.floor(v);
32224     },
32225
32226     beforeBlur : function()
32227     {
32228         if(!this.castInt){
32229             return;
32230         }
32231         
32232         var v = this.parseValue(this.getRawValue());
32233         if(v){
32234             this.setValue(v);
32235         }
32236     }
32237     
32238 });
32239
32240  
32241
32242 /*
32243 * Licence: LGPL
32244 */
32245
32246 /**
32247  * @class Roo.bootstrap.DocumentSlider
32248  * @extends Roo.bootstrap.Component
32249  * Bootstrap DocumentSlider class
32250  * 
32251  * @constructor
32252  * Create a new DocumentViewer
32253  * @param {Object} config The config object
32254  */
32255
32256 Roo.bootstrap.DocumentSlider = function(config){
32257     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
32258     
32259     this.files = [];
32260     
32261     this.addEvents({
32262         /**
32263          * @event initial
32264          * Fire after initEvent
32265          * @param {Roo.bootstrap.DocumentSlider} this
32266          */
32267         "initial" : true,
32268         /**
32269          * @event update
32270          * Fire after update
32271          * @param {Roo.bootstrap.DocumentSlider} this
32272          */
32273         "update" : true,
32274         /**
32275          * @event click
32276          * Fire after click
32277          * @param {Roo.bootstrap.DocumentSlider} this
32278          */
32279         "click" : true
32280     });
32281 };
32282
32283 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
32284     
32285     files : false,
32286     
32287     indicator : 0,
32288     
32289     getAutoCreate : function()
32290     {
32291         var cfg = {
32292             tag : 'div',
32293             cls : 'roo-document-slider',
32294             cn : [
32295                 {
32296                     tag : 'div',
32297                     cls : 'roo-document-slider-header',
32298                     cn : [
32299                         {
32300                             tag : 'div',
32301                             cls : 'roo-document-slider-header-title'
32302                         }
32303                     ]
32304                 },
32305                 {
32306                     tag : 'div',
32307                     cls : 'roo-document-slider-body',
32308                     cn : [
32309                         {
32310                             tag : 'div',
32311                             cls : 'roo-document-slider-prev',
32312                             cn : [
32313                                 {
32314                                     tag : 'i',
32315                                     cls : 'fa fa-chevron-left'
32316                                 }
32317                             ]
32318                         },
32319                         {
32320                             tag : 'div',
32321                             cls : 'roo-document-slider-thumb',
32322                             cn : [
32323                                 {
32324                                     tag : 'img',
32325                                     cls : 'roo-document-slider-image'
32326                                 }
32327                             ]
32328                         },
32329                         {
32330                             tag : 'div',
32331                             cls : 'roo-document-slider-next',
32332                             cn : [
32333                                 {
32334                                     tag : 'i',
32335                                     cls : 'fa fa-chevron-right'
32336                                 }
32337                             ]
32338                         }
32339                     ]
32340                 }
32341             ]
32342         };
32343         
32344         return cfg;
32345     },
32346     
32347     initEvents : function()
32348     {
32349         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
32350         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
32351         
32352         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
32353         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
32354         
32355         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
32356         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32357         
32358         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
32359         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32360         
32361         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
32362         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32363         
32364         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
32365         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
32366         
32367         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
32368         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
32369         
32370         this.thumbEl.on('click', this.onClick, this);
32371         
32372         this.prevIndicator.on('click', this.prev, this);
32373         
32374         this.nextIndicator.on('click', this.next, this);
32375         
32376     },
32377     
32378     initial : function()
32379     {
32380         if(this.files.length){
32381             this.indicator = 1;
32382             this.update()
32383         }
32384         
32385         this.fireEvent('initial', this);
32386     },
32387     
32388     update : function()
32389     {
32390         this.imageEl.attr('src', this.files[this.indicator - 1]);
32391         
32392         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
32393         
32394         this.prevIndicator.show();
32395         
32396         if(this.indicator == 1){
32397             this.prevIndicator.hide();
32398         }
32399         
32400         this.nextIndicator.show();
32401         
32402         if(this.indicator == this.files.length){
32403             this.nextIndicator.hide();
32404         }
32405         
32406         this.thumbEl.scrollTo('top');
32407         
32408         this.fireEvent('update', this);
32409     },
32410     
32411     onClick : function(e)
32412     {
32413         e.preventDefault();
32414         
32415         this.fireEvent('click', this);
32416     },
32417     
32418     prev : function(e)
32419     {
32420         e.preventDefault();
32421         
32422         this.indicator = Math.max(1, this.indicator - 1);
32423         
32424         this.update();
32425     },
32426     
32427     next : function(e)
32428     {
32429         e.preventDefault();
32430         
32431         this.indicator = Math.min(this.files.length, this.indicator + 1);
32432         
32433         this.update();
32434     }
32435 });
32436 /*
32437  * - LGPL
32438  *
32439  * RadioSet
32440  *
32441  *
32442  */
32443
32444 /**
32445  * @class Roo.bootstrap.RadioSet
32446  * @extends Roo.bootstrap.Input
32447  * Bootstrap RadioSet class
32448  * @cfg {String} indicatorpos (left|right) default left
32449  * @cfg {Boolean} inline (true|false) inline the element (default true)
32450  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
32451  * @constructor
32452  * Create a new RadioSet
32453  * @param {Object} config The config object
32454  */
32455
32456 Roo.bootstrap.RadioSet = function(config){
32457     
32458     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
32459     
32460     this.radioes = [];
32461     
32462     Roo.bootstrap.RadioSet.register(this);
32463     
32464     this.addEvents({
32465         /**
32466         * @event check
32467         * Fires when the element is checked or unchecked.
32468         * @param {Roo.bootstrap.RadioSet} this This radio
32469         * @param {Roo.bootstrap.Radio} item The checked item
32470         */
32471        check : true
32472     });
32473     
32474 };
32475
32476 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
32477
32478     radioes : false,
32479     
32480     inline : true,
32481     
32482     weight : '',
32483     
32484     indicatorpos : 'left',
32485     
32486     getAutoCreate : function()
32487     {
32488         var label = {
32489             tag : 'label',
32490             cls : 'roo-radio-set-label',
32491             cn : [
32492                 {
32493                     tag : 'span',
32494                     html : this.fieldLabel
32495                 }
32496             ]
32497         };
32498         
32499         if(this.indicatorpos == 'left'){
32500             label.cn.unshift({
32501                 tag : 'i',
32502                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
32503                 tooltip : 'This field is required'
32504             });
32505         } else {
32506             label.cn.push({
32507                 tag : 'i',
32508                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
32509                 tooltip : 'This field is required'
32510             });
32511         }
32512         
32513         var items = {
32514             tag : 'div',
32515             cls : 'roo-radio-set-items'
32516         };
32517         
32518         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
32519         
32520         if (align === 'left' && this.fieldLabel.length) {
32521             
32522             items = {
32523                 cls : "roo-radio-set-right", 
32524                 cn: [
32525                     items
32526                 ]
32527             };
32528             
32529             if(this.labelWidth > 12){
32530                 label.style = "width: " + this.labelWidth + 'px';
32531             }
32532             
32533             if(this.labelWidth < 13 && this.labelmd == 0){
32534                 this.labelmd = this.labelWidth;
32535             }
32536             
32537             if(this.labellg > 0){
32538                 label.cls += ' col-lg-' + this.labellg;
32539                 items.cls += ' col-lg-' + (12 - this.labellg);
32540             }
32541             
32542             if(this.labelmd > 0){
32543                 label.cls += ' col-md-' + this.labelmd;
32544                 items.cls += ' col-md-' + (12 - this.labelmd);
32545             }
32546             
32547             if(this.labelsm > 0){
32548                 label.cls += ' col-sm-' + this.labelsm;
32549                 items.cls += ' col-sm-' + (12 - this.labelsm);
32550             }
32551             
32552             if(this.labelxs > 0){
32553                 label.cls += ' col-xs-' + this.labelxs;
32554                 items.cls += ' col-xs-' + (12 - this.labelxs);
32555             }
32556         }
32557         
32558         var cfg = {
32559             tag : 'div',
32560             cls : 'roo-radio-set',
32561             cn : [
32562                 {
32563                     tag : 'input',
32564                     cls : 'roo-radio-set-input',
32565                     type : 'hidden',
32566                     name : this.name,
32567                     value : this.value ? this.value :  ''
32568                 },
32569                 label,
32570                 items
32571             ]
32572         };
32573         
32574         if(this.weight.length){
32575             cfg.cls += ' roo-radio-' + this.weight;
32576         }
32577         
32578         if(this.inline) {
32579             cfg.cls += ' roo-radio-set-inline';
32580         }
32581         
32582         return cfg;
32583         
32584     },
32585
32586     initEvents : function()
32587     {
32588         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
32589         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
32590         
32591         if(!this.fieldLabel.length){
32592             this.labelEl.hide();
32593         }
32594         
32595         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
32596         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
32597         
32598         this.indicatorEl().setVisibilityMode(Roo.Element.DISPLAY);
32599         this.indicatorEl().hide();
32600         
32601         this.originalValue = this.getValue();
32602         
32603     },
32604     
32605     inputEl: function ()
32606     {
32607         return this.el.select('.roo-radio-set-input', true).first();
32608     },
32609     
32610     getChildContainer : function()
32611     {
32612         return this.itemsEl;
32613     },
32614     
32615     register : function(item)
32616     {
32617         this.radioes.push(item);
32618         
32619     },
32620     
32621     validate : function()
32622     {   
32623         var valid = false;
32624         
32625         Roo.each(this.radioes, function(i){
32626             if(!i.checked){
32627                 return;
32628             }
32629             
32630             valid = true;
32631             return false;
32632         });
32633         
32634         if(this.allowBlank) {
32635             return true;
32636         }
32637         
32638         if(this.disabled || valid){
32639             this.markValid();
32640             return true;
32641         }
32642         
32643         this.markInvalid();
32644         return false;
32645         
32646     },
32647     
32648     markValid : function()
32649     {
32650         if(this.labelEl.isVisible(true)){
32651             this.indicatorEl().hide();
32652         }
32653         
32654         this.el.removeClass([this.invalidClass, this.validClass]);
32655         this.el.addClass(this.validClass);
32656         
32657         this.fireEvent('valid', this);
32658     },
32659     
32660     markInvalid : function(msg)
32661     {
32662         if(this.allowBlank || this.disabled){
32663             return;
32664         }
32665         
32666         if(this.labelEl.isVisible(true)){
32667             this.indicatorEl().show();
32668         }
32669         
32670         this.el.removeClass([this.invalidClass, this.validClass]);
32671         this.el.addClass(this.invalidClass);
32672         
32673         this.fireEvent('invalid', this, msg);
32674         
32675     },
32676     
32677     setValue : function(v, suppressEvent)
32678     {   
32679         Roo.each(this.radioes, function(i){
32680             
32681             i.checked = false;
32682             i.el.removeClass('checked');
32683             
32684             if(i.value === v || i.value.toString() === v.toString()){
32685                 i.checked = true;
32686                 i.el.addClass('checked');
32687                 
32688                 if(suppressEvent !== true){
32689                     this.fireEvent('check', this, i);
32690                 }
32691             }
32692             
32693         }, this);
32694         
32695         Roo.bootstrap.RadioSet.superclass.setValue.call(this, v);
32696         
32697     },
32698     
32699     clearInvalid : function(){
32700         
32701         if(!this.el || this.preventMark){
32702             return;
32703         }
32704         
32705         if(this.labelEl.isVisible(true)){
32706             this.indicatorEl().hide();
32707         }
32708         
32709         this.el.removeClass([this.invalidClass]);
32710         
32711         this.fireEvent('valid', this);
32712     }
32713     
32714 });
32715
32716 Roo.apply(Roo.bootstrap.RadioSet, {
32717     
32718     groups: {},
32719     
32720     register : function(set)
32721     {
32722         this.groups[set.name] = set;
32723     },
32724     
32725     get: function(name) 
32726     {
32727         if (typeof(this.groups[name]) == 'undefined') {
32728             return false;
32729         }
32730         
32731         return this.groups[name] ;
32732     }
32733     
32734 });
32735 /*
32736  * Based on:
32737  * Ext JS Library 1.1.1
32738  * Copyright(c) 2006-2007, Ext JS, LLC.
32739  *
32740  * Originally Released Under LGPL - original licence link has changed is not relivant.
32741  *
32742  * Fork - LGPL
32743  * <script type="text/javascript">
32744  */
32745
32746
32747 /**
32748  * @class Roo.bootstrap.SplitBar
32749  * @extends Roo.util.Observable
32750  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
32751  * <br><br>
32752  * Usage:
32753  * <pre><code>
32754 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
32755                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
32756 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
32757 split.minSize = 100;
32758 split.maxSize = 600;
32759 split.animate = true;
32760 split.on('moved', splitterMoved);
32761 </code></pre>
32762  * @constructor
32763  * Create a new SplitBar
32764  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
32765  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
32766  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32767  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
32768                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
32769                         position of the SplitBar).
32770  */
32771 Roo.bootstrap.SplitBar = function(cfg){
32772     
32773     /** @private */
32774     
32775     //{
32776     //  dragElement : elm
32777     //  resizingElement: el,
32778         // optional..
32779     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
32780     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
32781         // existingProxy ???
32782     //}
32783     
32784     this.el = Roo.get(cfg.dragElement, true);
32785     this.el.dom.unselectable = "on";
32786     /** @private */
32787     this.resizingEl = Roo.get(cfg.resizingElement, true);
32788
32789     /**
32790      * @private
32791      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32792      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
32793      * @type Number
32794      */
32795     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
32796     
32797     /**
32798      * The minimum size of the resizing element. (Defaults to 0)
32799      * @type Number
32800      */
32801     this.minSize = 0;
32802     
32803     /**
32804      * The maximum size of the resizing element. (Defaults to 2000)
32805      * @type Number
32806      */
32807     this.maxSize = 2000;
32808     
32809     /**
32810      * Whether to animate the transition to the new size
32811      * @type Boolean
32812      */
32813     this.animate = false;
32814     
32815     /**
32816      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
32817      * @type Boolean
32818      */
32819     this.useShim = false;
32820     
32821     /** @private */
32822     this.shim = null;
32823     
32824     if(!cfg.existingProxy){
32825         /** @private */
32826         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
32827     }else{
32828         this.proxy = Roo.get(cfg.existingProxy).dom;
32829     }
32830     /** @private */
32831     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
32832     
32833     /** @private */
32834     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
32835     
32836     /** @private */
32837     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
32838     
32839     /** @private */
32840     this.dragSpecs = {};
32841     
32842     /**
32843      * @private The adapter to use to positon and resize elements
32844      */
32845     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32846     this.adapter.init(this);
32847     
32848     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32849         /** @private */
32850         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
32851         this.el.addClass("roo-splitbar-h");
32852     }else{
32853         /** @private */
32854         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
32855         this.el.addClass("roo-splitbar-v");
32856     }
32857     
32858     this.addEvents({
32859         /**
32860          * @event resize
32861          * Fires when the splitter is moved (alias for {@link #event-moved})
32862          * @param {Roo.bootstrap.SplitBar} this
32863          * @param {Number} newSize the new width or height
32864          */
32865         "resize" : true,
32866         /**
32867          * @event moved
32868          * Fires when the splitter is moved
32869          * @param {Roo.bootstrap.SplitBar} this
32870          * @param {Number} newSize the new width or height
32871          */
32872         "moved" : true,
32873         /**
32874          * @event beforeresize
32875          * Fires before the splitter is dragged
32876          * @param {Roo.bootstrap.SplitBar} this
32877          */
32878         "beforeresize" : true,
32879
32880         "beforeapply" : true
32881     });
32882
32883     Roo.util.Observable.call(this);
32884 };
32885
32886 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
32887     onStartProxyDrag : function(x, y){
32888         this.fireEvent("beforeresize", this);
32889         if(!this.overlay){
32890             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
32891             o.unselectable();
32892             o.enableDisplayMode("block");
32893             // all splitbars share the same overlay
32894             Roo.bootstrap.SplitBar.prototype.overlay = o;
32895         }
32896         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32897         this.overlay.show();
32898         Roo.get(this.proxy).setDisplayed("block");
32899         var size = this.adapter.getElementSize(this);
32900         this.activeMinSize = this.getMinimumSize();;
32901         this.activeMaxSize = this.getMaximumSize();;
32902         var c1 = size - this.activeMinSize;
32903         var c2 = Math.max(this.activeMaxSize - size, 0);
32904         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32905             this.dd.resetConstraints();
32906             this.dd.setXConstraint(
32907                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
32908                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
32909             );
32910             this.dd.setYConstraint(0, 0);
32911         }else{
32912             this.dd.resetConstraints();
32913             this.dd.setXConstraint(0, 0);
32914             this.dd.setYConstraint(
32915                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
32916                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
32917             );
32918          }
32919         this.dragSpecs.startSize = size;
32920         this.dragSpecs.startPoint = [x, y];
32921         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
32922     },
32923     
32924     /** 
32925      * @private Called after the drag operation by the DDProxy
32926      */
32927     onEndProxyDrag : function(e){
32928         Roo.get(this.proxy).setDisplayed(false);
32929         var endPoint = Roo.lib.Event.getXY(e);
32930         if(this.overlay){
32931             this.overlay.hide();
32932         }
32933         var newSize;
32934         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32935             newSize = this.dragSpecs.startSize + 
32936                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
32937                     endPoint[0] - this.dragSpecs.startPoint[0] :
32938                     this.dragSpecs.startPoint[0] - endPoint[0]
32939                 );
32940         }else{
32941             newSize = this.dragSpecs.startSize + 
32942                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
32943                     endPoint[1] - this.dragSpecs.startPoint[1] :
32944                     this.dragSpecs.startPoint[1] - endPoint[1]
32945                 );
32946         }
32947         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
32948         if(newSize != this.dragSpecs.startSize){
32949             if(this.fireEvent('beforeapply', this, newSize) !== false){
32950                 this.adapter.setElementSize(this, newSize);
32951                 this.fireEvent("moved", this, newSize);
32952                 this.fireEvent("resize", this, newSize);
32953             }
32954         }
32955     },
32956     
32957     /**
32958      * Get the adapter this SplitBar uses
32959      * @return The adapter object
32960      */
32961     getAdapter : function(){
32962         return this.adapter;
32963     },
32964     
32965     /**
32966      * Set the adapter this SplitBar uses
32967      * @param {Object} adapter A SplitBar adapter object
32968      */
32969     setAdapter : function(adapter){
32970         this.adapter = adapter;
32971         this.adapter.init(this);
32972     },
32973     
32974     /**
32975      * Gets the minimum size for the resizing element
32976      * @return {Number} The minimum size
32977      */
32978     getMinimumSize : function(){
32979         return this.minSize;
32980     },
32981     
32982     /**
32983      * Sets the minimum size for the resizing element
32984      * @param {Number} minSize The minimum size
32985      */
32986     setMinimumSize : function(minSize){
32987         this.minSize = minSize;
32988     },
32989     
32990     /**
32991      * Gets the maximum size for the resizing element
32992      * @return {Number} The maximum size
32993      */
32994     getMaximumSize : function(){
32995         return this.maxSize;
32996     },
32997     
32998     /**
32999      * Sets the maximum size for the resizing element
33000      * @param {Number} maxSize The maximum size
33001      */
33002     setMaximumSize : function(maxSize){
33003         this.maxSize = maxSize;
33004     },
33005     
33006     /**
33007      * Sets the initialize size for the resizing element
33008      * @param {Number} size The initial size
33009      */
33010     setCurrentSize : function(size){
33011         var oldAnimate = this.animate;
33012         this.animate = false;
33013         this.adapter.setElementSize(this, size);
33014         this.animate = oldAnimate;
33015     },
33016     
33017     /**
33018      * Destroy this splitbar. 
33019      * @param {Boolean} removeEl True to remove the element
33020      */
33021     destroy : function(removeEl){
33022         if(this.shim){
33023             this.shim.remove();
33024         }
33025         this.dd.unreg();
33026         this.proxy.parentNode.removeChild(this.proxy);
33027         if(removeEl){
33028             this.el.remove();
33029         }
33030     }
33031 });
33032
33033 /**
33034  * @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.
33035  */
33036 Roo.bootstrap.SplitBar.createProxy = function(dir){
33037     var proxy = new Roo.Element(document.createElement("div"));
33038     proxy.unselectable();
33039     var cls = 'roo-splitbar-proxy';
33040     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33041     document.body.appendChild(proxy.dom);
33042     return proxy.dom;
33043 };
33044
33045 /** 
33046  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33047  * Default Adapter. It assumes the splitter and resizing element are not positioned
33048  * elements and only gets/sets the width of the element. Generally used for table based layouts.
33049  */
33050 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33051 };
33052
33053 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33054     // do nothing for now
33055     init : function(s){
33056     
33057     },
33058     /**
33059      * Called before drag operations to get the current size of the resizing element. 
33060      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33061      */
33062      getElementSize : function(s){
33063         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33064             return s.resizingEl.getWidth();
33065         }else{
33066             return s.resizingEl.getHeight();
33067         }
33068     },
33069     
33070     /**
33071      * Called after drag operations to set the size of the resizing element.
33072      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33073      * @param {Number} newSize The new size to set
33074      * @param {Function} onComplete A function to be invoked when resizing is complete
33075      */
33076     setElementSize : function(s, newSize, onComplete){
33077         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33078             if(!s.animate){
33079                 s.resizingEl.setWidth(newSize);
33080                 if(onComplete){
33081                     onComplete(s, newSize);
33082                 }
33083             }else{
33084                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33085             }
33086         }else{
33087             
33088             if(!s.animate){
33089                 s.resizingEl.setHeight(newSize);
33090                 if(onComplete){
33091                     onComplete(s, newSize);
33092                 }
33093             }else{
33094                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33095             }
33096         }
33097     }
33098 };
33099
33100 /** 
33101  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33102  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33103  * Adapter that  moves the splitter element to align with the resized sizing element. 
33104  * Used with an absolute positioned SplitBar.
33105  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33106  * document.body, make sure you assign an id to the body element.
33107  */
33108 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33109     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33110     this.container = Roo.get(container);
33111 };
33112
33113 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33114     init : function(s){
33115         this.basic.init(s);
33116     },
33117     
33118     getElementSize : function(s){
33119         return this.basic.getElementSize(s);
33120     },
33121     
33122     setElementSize : function(s, newSize, onComplete){
33123         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33124     },
33125     
33126     moveSplitter : function(s){
33127         var yes = Roo.bootstrap.SplitBar;
33128         switch(s.placement){
33129             case yes.LEFT:
33130                 s.el.setX(s.resizingEl.getRight());
33131                 break;
33132             case yes.RIGHT:
33133                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
33134                 break;
33135             case yes.TOP:
33136                 s.el.setY(s.resizingEl.getBottom());
33137                 break;
33138             case yes.BOTTOM:
33139                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
33140                 break;
33141         }
33142     }
33143 };
33144
33145 /**
33146  * Orientation constant - Create a vertical SplitBar
33147  * @static
33148  * @type Number
33149  */
33150 Roo.bootstrap.SplitBar.VERTICAL = 1;
33151
33152 /**
33153  * Orientation constant - Create a horizontal SplitBar
33154  * @static
33155  * @type Number
33156  */
33157 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
33158
33159 /**
33160  * Placement constant - The resizing element is to the left of the splitter element
33161  * @static
33162  * @type Number
33163  */
33164 Roo.bootstrap.SplitBar.LEFT = 1;
33165
33166 /**
33167  * Placement constant - The resizing element is to the right of the splitter element
33168  * @static
33169  * @type Number
33170  */
33171 Roo.bootstrap.SplitBar.RIGHT = 2;
33172
33173 /**
33174  * Placement constant - The resizing element is positioned above the splitter element
33175  * @static
33176  * @type Number
33177  */
33178 Roo.bootstrap.SplitBar.TOP = 3;
33179
33180 /**
33181  * Placement constant - The resizing element is positioned under splitter element
33182  * @static
33183  * @type Number
33184  */
33185 Roo.bootstrap.SplitBar.BOTTOM = 4;
33186 Roo.namespace("Roo.bootstrap.layout");/*
33187  * Based on:
33188  * Ext JS Library 1.1.1
33189  * Copyright(c) 2006-2007, Ext JS, LLC.
33190  *
33191  * Originally Released Under LGPL - original licence link has changed is not relivant.
33192  *
33193  * Fork - LGPL
33194  * <script type="text/javascript">
33195  */
33196
33197 /**
33198  * @class Roo.bootstrap.layout.Manager
33199  * @extends Roo.bootstrap.Component
33200  * Base class for layout managers.
33201  */
33202 Roo.bootstrap.layout.Manager = function(config)
33203 {
33204     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
33205
33206
33207
33208
33209
33210     /** false to disable window resize monitoring @type Boolean */
33211     this.monitorWindowResize = true;
33212     this.regions = {};
33213     this.addEvents({
33214         /**
33215          * @event layout
33216          * Fires when a layout is performed.
33217          * @param {Roo.LayoutManager} this
33218          */
33219         "layout" : true,
33220         /**
33221          * @event regionresized
33222          * Fires when the user resizes a region.
33223          * @param {Roo.LayoutRegion} region The resized region
33224          * @param {Number} newSize The new size (width for east/west, height for north/south)
33225          */
33226         "regionresized" : true,
33227         /**
33228          * @event regioncollapsed
33229          * Fires when a region is collapsed.
33230          * @param {Roo.LayoutRegion} region The collapsed region
33231          */
33232         "regioncollapsed" : true,
33233         /**
33234          * @event regionexpanded
33235          * Fires when a region is expanded.
33236          * @param {Roo.LayoutRegion} region The expanded region
33237          */
33238         "regionexpanded" : true
33239     });
33240     this.updating = false;
33241
33242     if (config.el) {
33243         this.el = Roo.get(config.el);
33244         this.initEvents();
33245     }
33246
33247 };
33248
33249 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
33250
33251
33252     regions : null,
33253
33254     monitorWindowResize : true,
33255
33256
33257     updating : false,
33258
33259
33260     onRender : function(ct, position)
33261     {
33262         if(!this.el){
33263             this.el = Roo.get(ct);
33264             this.initEvents();
33265         }
33266         //this.fireEvent('render',this);
33267     },
33268
33269
33270     initEvents: function()
33271     {
33272
33273
33274         // ie scrollbar fix
33275         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
33276             document.body.scroll = "no";
33277         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
33278             this.el.position('relative');
33279         }
33280         this.id = this.el.id;
33281         this.el.addClass("roo-layout-container");
33282         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33283         if(this.el.dom != document.body ) {
33284             this.el.on('resize', this.layout,this);
33285             this.el.on('show', this.layout,this);
33286         }
33287
33288     },
33289
33290     /**
33291      * Returns true if this layout is currently being updated
33292      * @return {Boolean}
33293      */
33294     isUpdating : function(){
33295         return this.updating;
33296     },
33297
33298     /**
33299      * Suspend the LayoutManager from doing auto-layouts while
33300      * making multiple add or remove calls
33301      */
33302     beginUpdate : function(){
33303         this.updating = true;
33304     },
33305
33306     /**
33307      * Restore auto-layouts and optionally disable the manager from performing a layout
33308      * @param {Boolean} noLayout true to disable a layout update
33309      */
33310     endUpdate : function(noLayout){
33311         this.updating = false;
33312         if(!noLayout){
33313             this.layout();
33314         }
33315     },
33316
33317     layout: function(){
33318         // abstract...
33319     },
33320
33321     onRegionResized : function(region, newSize){
33322         this.fireEvent("regionresized", region, newSize);
33323         this.layout();
33324     },
33325
33326     onRegionCollapsed : function(region){
33327         this.fireEvent("regioncollapsed", region);
33328     },
33329
33330     onRegionExpanded : function(region){
33331         this.fireEvent("regionexpanded", region);
33332     },
33333
33334     /**
33335      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
33336      * performs box-model adjustments.
33337      * @return {Object} The size as an object {width: (the width), height: (the height)}
33338      */
33339     getViewSize : function()
33340     {
33341         var size;
33342         if(this.el.dom != document.body){
33343             size = this.el.getSize();
33344         }else{
33345             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
33346         }
33347         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
33348         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
33349         return size;
33350     },
33351
33352     /**
33353      * Returns the Element this layout is bound to.
33354      * @return {Roo.Element}
33355      */
33356     getEl : function(){
33357         return this.el;
33358     },
33359
33360     /**
33361      * Returns the specified region.
33362      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
33363      * @return {Roo.LayoutRegion}
33364      */
33365     getRegion : function(target){
33366         return this.regions[target.toLowerCase()];
33367     },
33368
33369     onWindowResize : function(){
33370         if(this.monitorWindowResize){
33371             this.layout();
33372         }
33373     }
33374 });
33375 /*
33376  * Based on:
33377  * Ext JS Library 1.1.1
33378  * Copyright(c) 2006-2007, Ext JS, LLC.
33379  *
33380  * Originally Released Under LGPL - original licence link has changed is not relivant.
33381  *
33382  * Fork - LGPL
33383  * <script type="text/javascript">
33384  */
33385 /**
33386  * @class Roo.bootstrap.layout.Border
33387  * @extends Roo.bootstrap.layout.Manager
33388  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
33389  * please see: examples/bootstrap/nested.html<br><br>
33390  
33391 <b>The container the layout is rendered into can be either the body element or any other element.
33392 If it is not the body element, the container needs to either be an absolute positioned element,
33393 or you will need to add "position:relative" to the css of the container.  You will also need to specify
33394 the container size if it is not the body element.</b>
33395
33396 * @constructor
33397 * Create a new Border
33398 * @param {Object} config Configuration options
33399  */
33400 Roo.bootstrap.layout.Border = function(config){
33401     config = config || {};
33402     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
33403     
33404     
33405     
33406     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
33407         if(config[region]){
33408             config[region].region = region;
33409             this.addRegion(config[region]);
33410         }
33411     },this);
33412     
33413 };
33414
33415 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
33416
33417 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
33418     /**
33419      * Creates and adds a new region if it doesn't already exist.
33420      * @param {String} target The target region key (north, south, east, west or center).
33421      * @param {Object} config The regions config object
33422      * @return {BorderLayoutRegion} The new region
33423      */
33424     addRegion : function(config)
33425     {
33426         if(!this.regions[config.region]){
33427             var r = this.factory(config);
33428             this.bindRegion(r);
33429         }
33430         return this.regions[config.region];
33431     },
33432
33433     // private (kinda)
33434     bindRegion : function(r){
33435         this.regions[r.config.region] = r;
33436         
33437         r.on("visibilitychange",    this.layout, this);
33438         r.on("paneladded",          this.layout, this);
33439         r.on("panelremoved",        this.layout, this);
33440         r.on("invalidated",         this.layout, this);
33441         r.on("resized",             this.onRegionResized, this);
33442         r.on("collapsed",           this.onRegionCollapsed, this);
33443         r.on("expanded",            this.onRegionExpanded, this);
33444     },
33445
33446     /**
33447      * Performs a layout update.
33448      */
33449     layout : function()
33450     {
33451         if(this.updating) {
33452             return;
33453         }
33454         
33455         // render all the rebions if they have not been done alreayd?
33456         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
33457             if(this.regions[region] && !this.regions[region].bodyEl){
33458                 this.regions[region].onRender(this.el)
33459             }
33460         },this);
33461         
33462         var size = this.getViewSize();
33463         var w = size.width;
33464         var h = size.height;
33465         var centerW = w;
33466         var centerH = h;
33467         var centerY = 0;
33468         var centerX = 0;
33469         //var x = 0, y = 0;
33470
33471         var rs = this.regions;
33472         var north = rs["north"];
33473         var south = rs["south"]; 
33474         var west = rs["west"];
33475         var east = rs["east"];
33476         var center = rs["center"];
33477         //if(this.hideOnLayout){ // not supported anymore
33478             //c.el.setStyle("display", "none");
33479         //}
33480         if(north && north.isVisible()){
33481             var b = north.getBox();
33482             var m = north.getMargins();
33483             b.width = w - (m.left+m.right);
33484             b.x = m.left;
33485             b.y = m.top;
33486             centerY = b.height + b.y + m.bottom;
33487             centerH -= centerY;
33488             north.updateBox(this.safeBox(b));
33489         }
33490         if(south && south.isVisible()){
33491             var b = south.getBox();
33492             var m = south.getMargins();
33493             b.width = w - (m.left+m.right);
33494             b.x = m.left;
33495             var totalHeight = (b.height + m.top + m.bottom);
33496             b.y = h - totalHeight + m.top;
33497             centerH -= totalHeight;
33498             south.updateBox(this.safeBox(b));
33499         }
33500         if(west && west.isVisible()){
33501             var b = west.getBox();
33502             var m = west.getMargins();
33503             b.height = centerH - (m.top+m.bottom);
33504             b.x = m.left;
33505             b.y = centerY + m.top;
33506             var totalWidth = (b.width + m.left + m.right);
33507             centerX += totalWidth;
33508             centerW -= totalWidth;
33509             west.updateBox(this.safeBox(b));
33510         }
33511         if(east && east.isVisible()){
33512             var b = east.getBox();
33513             var m = east.getMargins();
33514             b.height = centerH - (m.top+m.bottom);
33515             var totalWidth = (b.width + m.left + m.right);
33516             b.x = w - totalWidth + m.left;
33517             b.y = centerY + m.top;
33518             centerW -= totalWidth;
33519             east.updateBox(this.safeBox(b));
33520         }
33521         if(center){
33522             var m = center.getMargins();
33523             var centerBox = {
33524                 x: centerX + m.left,
33525                 y: centerY + m.top,
33526                 width: centerW - (m.left+m.right),
33527                 height: centerH - (m.top+m.bottom)
33528             };
33529             //if(this.hideOnLayout){
33530                 //center.el.setStyle("display", "block");
33531             //}
33532             center.updateBox(this.safeBox(centerBox));
33533         }
33534         this.el.repaint();
33535         this.fireEvent("layout", this);
33536     },
33537
33538     // private
33539     safeBox : function(box){
33540         box.width = Math.max(0, box.width);
33541         box.height = Math.max(0, box.height);
33542         return box;
33543     },
33544
33545     /**
33546      * Adds a ContentPanel (or subclass) to this layout.
33547      * @param {String} target The target region key (north, south, east, west or center).
33548      * @param {Roo.ContentPanel} panel The panel to add
33549      * @return {Roo.ContentPanel} The added panel
33550      */
33551     add : function(target, panel){
33552          
33553         target = target.toLowerCase();
33554         return this.regions[target].add(panel);
33555     },
33556
33557     /**
33558      * Remove a ContentPanel (or subclass) to this layout.
33559      * @param {String} target The target region key (north, south, east, west or center).
33560      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
33561      * @return {Roo.ContentPanel} The removed panel
33562      */
33563     remove : function(target, panel){
33564         target = target.toLowerCase();
33565         return this.regions[target].remove(panel);
33566     },
33567
33568     /**
33569      * Searches all regions for a panel with the specified id
33570      * @param {String} panelId
33571      * @return {Roo.ContentPanel} The panel or null if it wasn't found
33572      */
33573     findPanel : function(panelId){
33574         var rs = this.regions;
33575         for(var target in rs){
33576             if(typeof rs[target] != "function"){
33577                 var p = rs[target].getPanel(panelId);
33578                 if(p){
33579                     return p;
33580                 }
33581             }
33582         }
33583         return null;
33584     },
33585
33586     /**
33587      * Searches all regions for a panel with the specified id and activates (shows) it.
33588      * @param {String/ContentPanel} panelId The panels id or the panel itself
33589      * @return {Roo.ContentPanel} The shown panel or null
33590      */
33591     showPanel : function(panelId) {
33592       var rs = this.regions;
33593       for(var target in rs){
33594          var r = rs[target];
33595          if(typeof r != "function"){
33596             if(r.hasPanel(panelId)){
33597                return r.showPanel(panelId);
33598             }
33599          }
33600       }
33601       return null;
33602    },
33603
33604    /**
33605      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
33606      * @param {Roo.state.Provider} provider (optional) An alternate state provider
33607      */
33608    /*
33609     restoreState : function(provider){
33610         if(!provider){
33611             provider = Roo.state.Manager;
33612         }
33613         var sm = new Roo.LayoutStateManager();
33614         sm.init(this, provider);
33615     },
33616 */
33617  
33618  
33619     /**
33620      * Adds a xtype elements to the layout.
33621      * <pre><code>
33622
33623 layout.addxtype({
33624        xtype : 'ContentPanel',
33625        region: 'west',
33626        items: [ .... ]
33627    }
33628 );
33629
33630 layout.addxtype({
33631         xtype : 'NestedLayoutPanel',
33632         region: 'west',
33633         layout: {
33634            center: { },
33635            west: { }   
33636         },
33637         items : [ ... list of content panels or nested layout panels.. ]
33638    }
33639 );
33640 </code></pre>
33641      * @param {Object} cfg Xtype definition of item to add.
33642      */
33643     addxtype : function(cfg)
33644     {
33645         // basically accepts a pannel...
33646         // can accept a layout region..!?!?
33647         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
33648         
33649         
33650         // theory?  children can only be panels??
33651         
33652         //if (!cfg.xtype.match(/Panel$/)) {
33653         //    return false;
33654         //}
33655         var ret = false;
33656         
33657         if (typeof(cfg.region) == 'undefined') {
33658             Roo.log("Failed to add Panel, region was not set");
33659             Roo.log(cfg);
33660             return false;
33661         }
33662         var region = cfg.region;
33663         delete cfg.region;
33664         
33665           
33666         var xitems = [];
33667         if (cfg.items) {
33668             xitems = cfg.items;
33669             delete cfg.items;
33670         }
33671         var nb = false;
33672         
33673         switch(cfg.xtype) 
33674         {
33675             case 'Content':  // ContentPanel (el, cfg)
33676             case 'Scroll':  // ContentPanel (el, cfg)
33677             case 'View': 
33678                 cfg.autoCreate = true;
33679                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33680                 //} else {
33681                 //    var el = this.el.createChild();
33682                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
33683                 //}
33684                 
33685                 this.add(region, ret);
33686                 break;
33687             
33688             /*
33689             case 'TreePanel': // our new panel!
33690                 cfg.el = this.el.createChild();
33691                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33692                 this.add(region, ret);
33693                 break;
33694             */
33695             
33696             case 'Nest': 
33697                 // create a new Layout (which is  a Border Layout...
33698                 
33699                 var clayout = cfg.layout;
33700                 clayout.el  = this.el.createChild();
33701                 clayout.items   = clayout.items  || [];
33702                 
33703                 delete cfg.layout;
33704                 
33705                 // replace this exitems with the clayout ones..
33706                 xitems = clayout.items;
33707                  
33708                 // force background off if it's in center...
33709                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33710                     cfg.background = false;
33711                 }
33712                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
33713                 
33714                 
33715                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33716                 //console.log('adding nested layout panel '  + cfg.toSource());
33717                 this.add(region, ret);
33718                 nb = {}; /// find first...
33719                 break;
33720             
33721             case 'Grid':
33722                 
33723                 // needs grid and region
33724                 
33725                 //var el = this.getRegion(region).el.createChild();
33726                 /*
33727                  *var el = this.el.createChild();
33728                 // create the grid first...
33729                 cfg.grid.container = el;
33730                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
33731                 */
33732                 
33733                 if (region == 'center' && this.active ) {
33734                     cfg.background = false;
33735                 }
33736                 
33737                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33738                 
33739                 this.add(region, ret);
33740                 /*
33741                 if (cfg.background) {
33742                     // render grid on panel activation (if panel background)
33743                     ret.on('activate', function(gp) {
33744                         if (!gp.grid.rendered) {
33745                     //        gp.grid.render(el);
33746                         }
33747                     });
33748                 } else {
33749                   //  cfg.grid.render(el);
33750                 }
33751                 */
33752                 break;
33753            
33754            
33755             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
33756                 // it was the old xcomponent building that caused this before.
33757                 // espeically if border is the top element in the tree.
33758                 ret = this;
33759                 break; 
33760                 
33761                     
33762                 
33763                 
33764                 
33765             default:
33766                 /*
33767                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33768                     
33769                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33770                     this.add(region, ret);
33771                 } else {
33772                 */
33773                     Roo.log(cfg);
33774                     throw "Can not add '" + cfg.xtype + "' to Border";
33775                     return null;
33776              
33777                                 
33778              
33779         }
33780         this.beginUpdate();
33781         // add children..
33782         var region = '';
33783         var abn = {};
33784         Roo.each(xitems, function(i)  {
33785             region = nb && i.region ? i.region : false;
33786             
33787             var add = ret.addxtype(i);
33788            
33789             if (region) {
33790                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33791                 if (!i.background) {
33792                     abn[region] = nb[region] ;
33793                 }
33794             }
33795             
33796         });
33797         this.endUpdate();
33798
33799         // make the last non-background panel active..
33800         //if (nb) { Roo.log(abn); }
33801         if (nb) {
33802             
33803             for(var r in abn) {
33804                 region = this.getRegion(r);
33805                 if (region) {
33806                     // tried using nb[r], but it does not work..
33807                      
33808                     region.showPanel(abn[r]);
33809                    
33810                 }
33811             }
33812         }
33813         return ret;
33814         
33815     },
33816     
33817     
33818 // private
33819     factory : function(cfg)
33820     {
33821         
33822         var validRegions = Roo.bootstrap.layout.Border.regions;
33823
33824         var target = cfg.region;
33825         cfg.mgr = this;
33826         
33827         var r = Roo.bootstrap.layout;
33828         Roo.log(target);
33829         switch(target){
33830             case "north":
33831                 return new r.North(cfg);
33832             case "south":
33833                 return new r.South(cfg);
33834             case "east":
33835                 return new r.East(cfg);
33836             case "west":
33837                 return new r.West(cfg);
33838             case "center":
33839                 return new r.Center(cfg);
33840         }
33841         throw 'Layout region "'+target+'" not supported.';
33842     }
33843     
33844     
33845 });
33846  /*
33847  * Based on:
33848  * Ext JS Library 1.1.1
33849  * Copyright(c) 2006-2007, Ext JS, LLC.
33850  *
33851  * Originally Released Under LGPL - original licence link has changed is not relivant.
33852  *
33853  * Fork - LGPL
33854  * <script type="text/javascript">
33855  */
33856  
33857 /**
33858  * @class Roo.bootstrap.layout.Basic
33859  * @extends Roo.util.Observable
33860  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33861  * and does not have a titlebar, tabs or any other features. All it does is size and position 
33862  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33863  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
33864  * @cfg {string}   region  the region that it inhabits..
33865  * @cfg {bool}   skipConfig skip config?
33866  * 
33867
33868  */
33869 Roo.bootstrap.layout.Basic = function(config){
33870     
33871     this.mgr = config.mgr;
33872     
33873     this.position = config.region;
33874     
33875     var skipConfig = config.skipConfig;
33876     
33877     this.events = {
33878         /**
33879          * @scope Roo.BasicLayoutRegion
33880          */
33881         
33882         /**
33883          * @event beforeremove
33884          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33885          * @param {Roo.LayoutRegion} this
33886          * @param {Roo.ContentPanel} panel The panel
33887          * @param {Object} e The cancel event object
33888          */
33889         "beforeremove" : true,
33890         /**
33891          * @event invalidated
33892          * Fires when the layout for this region is changed.
33893          * @param {Roo.LayoutRegion} this
33894          */
33895         "invalidated" : true,
33896         /**
33897          * @event visibilitychange
33898          * Fires when this region is shown or hidden 
33899          * @param {Roo.LayoutRegion} this
33900          * @param {Boolean} visibility true or false
33901          */
33902         "visibilitychange" : true,
33903         /**
33904          * @event paneladded
33905          * Fires when a panel is added. 
33906          * @param {Roo.LayoutRegion} this
33907          * @param {Roo.ContentPanel} panel The panel
33908          */
33909         "paneladded" : true,
33910         /**
33911          * @event panelremoved
33912          * Fires when a panel is removed. 
33913          * @param {Roo.LayoutRegion} this
33914          * @param {Roo.ContentPanel} panel The panel
33915          */
33916         "panelremoved" : true,
33917         /**
33918          * @event beforecollapse
33919          * Fires when this region before collapse.
33920          * @param {Roo.LayoutRegion} this
33921          */
33922         "beforecollapse" : true,
33923         /**
33924          * @event collapsed
33925          * Fires when this region is collapsed.
33926          * @param {Roo.LayoutRegion} this
33927          */
33928         "collapsed" : true,
33929         /**
33930          * @event expanded
33931          * Fires when this region is expanded.
33932          * @param {Roo.LayoutRegion} this
33933          */
33934         "expanded" : true,
33935         /**
33936          * @event slideshow
33937          * Fires when this region is slid into view.
33938          * @param {Roo.LayoutRegion} this
33939          */
33940         "slideshow" : true,
33941         /**
33942          * @event slidehide
33943          * Fires when this region slides out of view. 
33944          * @param {Roo.LayoutRegion} this
33945          */
33946         "slidehide" : true,
33947         /**
33948          * @event panelactivated
33949          * Fires when a panel is activated. 
33950          * @param {Roo.LayoutRegion} this
33951          * @param {Roo.ContentPanel} panel The activated panel
33952          */
33953         "panelactivated" : true,
33954         /**
33955          * @event resized
33956          * Fires when the user resizes this region. 
33957          * @param {Roo.LayoutRegion} this
33958          * @param {Number} newSize The new size (width for east/west, height for north/south)
33959          */
33960         "resized" : true
33961     };
33962     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33963     this.panels = new Roo.util.MixedCollection();
33964     this.panels.getKey = this.getPanelId.createDelegate(this);
33965     this.box = null;
33966     this.activePanel = null;
33967     // ensure listeners are added...
33968     
33969     if (config.listeners || config.events) {
33970         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
33971             listeners : config.listeners || {},
33972             events : config.events || {}
33973         });
33974     }
33975     
33976     if(skipConfig !== true){
33977         this.applyConfig(config);
33978     }
33979 };
33980
33981 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
33982 {
33983     getPanelId : function(p){
33984         return p.getId();
33985     },
33986     
33987     applyConfig : function(config){
33988         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33989         this.config = config;
33990         
33991     },
33992     
33993     /**
33994      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33995      * the width, for horizontal (north, south) the height.
33996      * @param {Number} newSize The new width or height
33997      */
33998     resizeTo : function(newSize){
33999         var el = this.el ? this.el :
34000                  (this.activePanel ? this.activePanel.getEl() : null);
34001         if(el){
34002             switch(this.position){
34003                 case "east":
34004                 case "west":
34005                     el.setWidth(newSize);
34006                     this.fireEvent("resized", this, newSize);
34007                 break;
34008                 case "north":
34009                 case "south":
34010                     el.setHeight(newSize);
34011                     this.fireEvent("resized", this, newSize);
34012                 break;                
34013             }
34014         }
34015     },
34016     
34017     getBox : function(){
34018         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34019     },
34020     
34021     getMargins : function(){
34022         return this.margins;
34023     },
34024     
34025     updateBox : function(box){
34026         this.box = box;
34027         var el = this.activePanel.getEl();
34028         el.dom.style.left = box.x + "px";
34029         el.dom.style.top = box.y + "px";
34030         this.activePanel.setSize(box.width, box.height);
34031     },
34032     
34033     /**
34034      * Returns the container element for this region.
34035      * @return {Roo.Element}
34036      */
34037     getEl : function(){
34038         return this.activePanel;
34039     },
34040     
34041     /**
34042      * Returns true if this region is currently visible.
34043      * @return {Boolean}
34044      */
34045     isVisible : function(){
34046         return this.activePanel ? true : false;
34047     },
34048     
34049     setActivePanel : function(panel){
34050         panel = this.getPanel(panel);
34051         if(this.activePanel && this.activePanel != panel){
34052             this.activePanel.setActiveState(false);
34053             this.activePanel.getEl().setLeftTop(-10000,-10000);
34054         }
34055         this.activePanel = panel;
34056         panel.setActiveState(true);
34057         if(this.box){
34058             panel.setSize(this.box.width, this.box.height);
34059         }
34060         this.fireEvent("panelactivated", this, panel);
34061         this.fireEvent("invalidated");
34062     },
34063     
34064     /**
34065      * Show the specified panel.
34066      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34067      * @return {Roo.ContentPanel} The shown panel or null
34068      */
34069     showPanel : function(panel){
34070         panel = this.getPanel(panel);
34071         if(panel){
34072             this.setActivePanel(panel);
34073         }
34074         return panel;
34075     },
34076     
34077     /**
34078      * Get the active panel for this region.
34079      * @return {Roo.ContentPanel} The active panel or null
34080      */
34081     getActivePanel : function(){
34082         return this.activePanel;
34083     },
34084     
34085     /**
34086      * Add the passed ContentPanel(s)
34087      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34088      * @return {Roo.ContentPanel} The panel added (if only one was added)
34089      */
34090     add : function(panel){
34091         if(arguments.length > 1){
34092             for(var i = 0, len = arguments.length; i < len; i++) {
34093                 this.add(arguments[i]);
34094             }
34095             return null;
34096         }
34097         if(this.hasPanel(panel)){
34098             this.showPanel(panel);
34099             return panel;
34100         }
34101         var el = panel.getEl();
34102         if(el.dom.parentNode != this.mgr.el.dom){
34103             this.mgr.el.dom.appendChild(el.dom);
34104         }
34105         if(panel.setRegion){
34106             panel.setRegion(this);
34107         }
34108         this.panels.add(panel);
34109         el.setStyle("position", "absolute");
34110         if(!panel.background){
34111             this.setActivePanel(panel);
34112             if(this.config.initialSize && this.panels.getCount()==1){
34113                 this.resizeTo(this.config.initialSize);
34114             }
34115         }
34116         this.fireEvent("paneladded", this, panel);
34117         return panel;
34118     },
34119     
34120     /**
34121      * Returns true if the panel is in this region.
34122      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34123      * @return {Boolean}
34124      */
34125     hasPanel : function(panel){
34126         if(typeof panel == "object"){ // must be panel obj
34127             panel = panel.getId();
34128         }
34129         return this.getPanel(panel) ? true : false;
34130     },
34131     
34132     /**
34133      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34134      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34135      * @param {Boolean} preservePanel Overrides the config preservePanel option
34136      * @return {Roo.ContentPanel} The panel that was removed
34137      */
34138     remove : function(panel, preservePanel){
34139         panel = this.getPanel(panel);
34140         if(!panel){
34141             return null;
34142         }
34143         var e = {};
34144         this.fireEvent("beforeremove", this, panel, e);
34145         if(e.cancel === true){
34146             return null;
34147         }
34148         var panelId = panel.getId();
34149         this.panels.removeKey(panelId);
34150         return panel;
34151     },
34152     
34153     /**
34154      * Returns the panel specified or null if it's not in this region.
34155      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34156      * @return {Roo.ContentPanel}
34157      */
34158     getPanel : function(id){
34159         if(typeof id == "object"){ // must be panel obj
34160             return id;
34161         }
34162         return this.panels.get(id);
34163     },
34164     
34165     /**
34166      * Returns this regions position (north/south/east/west/center).
34167      * @return {String} 
34168      */
34169     getPosition: function(){
34170         return this.position;    
34171     }
34172 });/*
34173  * Based on:
34174  * Ext JS Library 1.1.1
34175  * Copyright(c) 2006-2007, Ext JS, LLC.
34176  *
34177  * Originally Released Under LGPL - original licence link has changed is not relivant.
34178  *
34179  * Fork - LGPL
34180  * <script type="text/javascript">
34181  */
34182  
34183 /**
34184  * @class Roo.bootstrap.layout.Region
34185  * @extends Roo.bootstrap.layout.Basic
34186  * This class represents a region in a layout manager.
34187  
34188  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34189  * @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})
34190  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
34191  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
34192  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
34193  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
34194  * @cfg {String}    title           The title for the region (overrides panel titles)
34195  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
34196  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34197  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
34198  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34199  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
34200  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34201  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
34202  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
34203  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
34204  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
34205
34206  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
34207  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
34208  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
34209  * @cfg {Number}    width           For East/West panels
34210  * @cfg {Number}    height          For North/South panels
34211  * @cfg {Boolean}   split           To show the splitter
34212  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
34213  * 
34214  * @cfg {string}   cls             Extra CSS classes to add to region
34215  * 
34216  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34217  * @cfg {string}   region  the region that it inhabits..
34218  *
34219
34220  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
34221  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
34222
34223  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
34224  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
34225  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
34226  */
34227 Roo.bootstrap.layout.Region = function(config)
34228 {
34229     this.applyConfig(config);
34230
34231     var mgr = config.mgr;
34232     var pos = config.region;
34233     config.skipConfig = true;
34234     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
34235     
34236     if (mgr.el) {
34237         this.onRender(mgr.el);   
34238     }
34239      
34240     this.visible = true;
34241     this.collapsed = false;
34242     this.unrendered_panels = [];
34243 };
34244
34245 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
34246
34247     position: '', // set by wrapper (eg. north/south etc..)
34248     unrendered_panels : null,  // unrendered panels.
34249     createBody : function(){
34250         /** This region's body element 
34251         * @type Roo.Element */
34252         this.bodyEl = this.el.createChild({
34253                 tag: "div",
34254                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
34255         });
34256     },
34257
34258     onRender: function(ctr, pos)
34259     {
34260         var dh = Roo.DomHelper;
34261         /** This region's container element 
34262         * @type Roo.Element */
34263         this.el = dh.append(ctr.dom, {
34264                 tag: "div",
34265                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
34266             }, true);
34267         /** This region's title element 
34268         * @type Roo.Element */
34269     
34270         this.titleEl = dh.append(this.el.dom,
34271             {
34272                     tag: "div",
34273                     unselectable: "on",
34274                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
34275                     children:[
34276                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
34277                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
34278                     ]}, true);
34279         
34280         this.titleEl.enableDisplayMode();
34281         /** This region's title text element 
34282         * @type HTMLElement */
34283         this.titleTextEl = this.titleEl.dom.firstChild;
34284         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
34285         /*
34286         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
34287         this.closeBtn.enableDisplayMode();
34288         this.closeBtn.on("click", this.closeClicked, this);
34289         this.closeBtn.hide();
34290     */
34291         this.createBody(this.config);
34292         if(this.config.hideWhenEmpty){
34293             this.hide();
34294             this.on("paneladded", this.validateVisibility, this);
34295             this.on("panelremoved", this.validateVisibility, this);
34296         }
34297         if(this.autoScroll){
34298             this.bodyEl.setStyle("overflow", "auto");
34299         }else{
34300             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
34301         }
34302         //if(c.titlebar !== false){
34303             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
34304                 this.titleEl.hide();
34305             }else{
34306                 this.titleEl.show();
34307                 if(this.config.title){
34308                     this.titleTextEl.innerHTML = this.config.title;
34309                 }
34310             }
34311         //}
34312         if(this.config.collapsed){
34313             this.collapse(true);
34314         }
34315         if(this.config.hidden){
34316             this.hide();
34317         }
34318         
34319         if (this.unrendered_panels && this.unrendered_panels.length) {
34320             for (var i =0;i< this.unrendered_panels.length; i++) {
34321                 this.add(this.unrendered_panels[i]);
34322             }
34323             this.unrendered_panels = null;
34324             
34325         }
34326         
34327     },
34328     
34329     applyConfig : function(c)
34330     {
34331         /*
34332          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
34333             var dh = Roo.DomHelper;
34334             if(c.titlebar !== false){
34335                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
34336                 this.collapseBtn.on("click", this.collapse, this);
34337                 this.collapseBtn.enableDisplayMode();
34338                 /*
34339                 if(c.showPin === true || this.showPin){
34340                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
34341                     this.stickBtn.enableDisplayMode();
34342                     this.stickBtn.on("click", this.expand, this);
34343                     this.stickBtn.hide();
34344                 }
34345                 
34346             }
34347             */
34348             /** This region's collapsed element
34349             * @type Roo.Element */
34350             /*
34351              *
34352             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
34353                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
34354             ]}, true);
34355             
34356             if(c.floatable !== false){
34357                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
34358                this.collapsedEl.on("click", this.collapseClick, this);
34359             }
34360
34361             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
34362                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
34363                    id: "message", unselectable: "on", style:{"float":"left"}});
34364                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
34365              }
34366             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
34367             this.expandBtn.on("click", this.expand, this);
34368             
34369         }
34370         
34371         if(this.collapseBtn){
34372             this.collapseBtn.setVisible(c.collapsible == true);
34373         }
34374         
34375         this.cmargins = c.cmargins || this.cmargins ||
34376                          (this.position == "west" || this.position == "east" ?
34377                              {top: 0, left: 2, right:2, bottom: 0} :
34378                              {top: 2, left: 0, right:0, bottom: 2});
34379         */
34380         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34381         
34382         
34383         this.bottomTabs = c.tabPosition != "top";
34384         
34385         this.autoScroll = c.autoScroll || false;
34386         
34387         
34388        
34389         
34390         this.duration = c.duration || .30;
34391         this.slideDuration = c.slideDuration || .45;
34392         this.config = c;
34393        
34394     },
34395     /**
34396      * Returns true if this region is currently visible.
34397      * @return {Boolean}
34398      */
34399     isVisible : function(){
34400         return this.visible;
34401     },
34402
34403     /**
34404      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
34405      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
34406      */
34407     //setCollapsedTitle : function(title){
34408     //    title = title || "&#160;";
34409      //   if(this.collapsedTitleTextEl){
34410       //      this.collapsedTitleTextEl.innerHTML = title;
34411        // }
34412     //},
34413
34414     getBox : function(){
34415         var b;
34416       //  if(!this.collapsed){
34417             b = this.el.getBox(false, true);
34418        // }else{
34419           //  b = this.collapsedEl.getBox(false, true);
34420         //}
34421         return b;
34422     },
34423
34424     getMargins : function(){
34425         return this.margins;
34426         //return this.collapsed ? this.cmargins : this.margins;
34427     },
34428 /*
34429     highlight : function(){
34430         this.el.addClass("x-layout-panel-dragover");
34431     },
34432
34433     unhighlight : function(){
34434         this.el.removeClass("x-layout-panel-dragover");
34435     },
34436 */
34437     updateBox : function(box)
34438     {
34439         if (!this.bodyEl) {
34440             return; // not rendered yet..
34441         }
34442         
34443         this.box = box;
34444         if(!this.collapsed){
34445             this.el.dom.style.left = box.x + "px";
34446             this.el.dom.style.top = box.y + "px";
34447             this.updateBody(box.width, box.height);
34448         }else{
34449             this.collapsedEl.dom.style.left = box.x + "px";
34450             this.collapsedEl.dom.style.top = box.y + "px";
34451             this.collapsedEl.setSize(box.width, box.height);
34452         }
34453         if(this.tabs){
34454             this.tabs.autoSizeTabs();
34455         }
34456     },
34457
34458     updateBody : function(w, h)
34459     {
34460         if(w !== null){
34461             this.el.setWidth(w);
34462             w -= this.el.getBorderWidth("rl");
34463             if(this.config.adjustments){
34464                 w += this.config.adjustments[0];
34465             }
34466         }
34467         if(h !== null && h > 0){
34468             this.el.setHeight(h);
34469             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
34470             h -= this.el.getBorderWidth("tb");
34471             if(this.config.adjustments){
34472                 h += this.config.adjustments[1];
34473             }
34474             this.bodyEl.setHeight(h);
34475             if(this.tabs){
34476                 h = this.tabs.syncHeight(h);
34477             }
34478         }
34479         if(this.panelSize){
34480             w = w !== null ? w : this.panelSize.width;
34481             h = h !== null ? h : this.panelSize.height;
34482         }
34483         if(this.activePanel){
34484             var el = this.activePanel.getEl();
34485             w = w !== null ? w : el.getWidth();
34486             h = h !== null ? h : el.getHeight();
34487             this.panelSize = {width: w, height: h};
34488             this.activePanel.setSize(w, h);
34489         }
34490         if(Roo.isIE && this.tabs){
34491             this.tabs.el.repaint();
34492         }
34493     },
34494
34495     /**
34496      * Returns the container element for this region.
34497      * @return {Roo.Element}
34498      */
34499     getEl : function(){
34500         return this.el;
34501     },
34502
34503     /**
34504      * Hides this region.
34505      */
34506     hide : function(){
34507         //if(!this.collapsed){
34508             this.el.dom.style.left = "-2000px";
34509             this.el.hide();
34510         //}else{
34511          //   this.collapsedEl.dom.style.left = "-2000px";
34512          //   this.collapsedEl.hide();
34513        // }
34514         this.visible = false;
34515         this.fireEvent("visibilitychange", this, false);
34516     },
34517
34518     /**
34519      * Shows this region if it was previously hidden.
34520      */
34521     show : function(){
34522         //if(!this.collapsed){
34523             this.el.show();
34524         //}else{
34525         //    this.collapsedEl.show();
34526        // }
34527         this.visible = true;
34528         this.fireEvent("visibilitychange", this, true);
34529     },
34530 /*
34531     closeClicked : function(){
34532         if(this.activePanel){
34533             this.remove(this.activePanel);
34534         }
34535     },
34536
34537     collapseClick : function(e){
34538         if(this.isSlid){
34539            e.stopPropagation();
34540            this.slideIn();
34541         }else{
34542            e.stopPropagation();
34543            this.slideOut();
34544         }
34545     },
34546 */
34547     /**
34548      * Collapses this region.
34549      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
34550      */
34551     /*
34552     collapse : function(skipAnim, skipCheck = false){
34553         if(this.collapsed) {
34554             return;
34555         }
34556         
34557         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
34558             
34559             this.collapsed = true;
34560             if(this.split){
34561                 this.split.el.hide();
34562             }
34563             if(this.config.animate && skipAnim !== true){
34564                 this.fireEvent("invalidated", this);
34565                 this.animateCollapse();
34566             }else{
34567                 this.el.setLocation(-20000,-20000);
34568                 this.el.hide();
34569                 this.collapsedEl.show();
34570                 this.fireEvent("collapsed", this);
34571                 this.fireEvent("invalidated", this);
34572             }
34573         }
34574         
34575     },
34576 */
34577     animateCollapse : function(){
34578         // overridden
34579     },
34580
34581     /**
34582      * Expands this region if it was previously collapsed.
34583      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
34584      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
34585      */
34586     /*
34587     expand : function(e, skipAnim){
34588         if(e) {
34589             e.stopPropagation();
34590         }
34591         if(!this.collapsed || this.el.hasActiveFx()) {
34592             return;
34593         }
34594         if(this.isSlid){
34595             this.afterSlideIn();
34596             skipAnim = true;
34597         }
34598         this.collapsed = false;
34599         if(this.config.animate && skipAnim !== true){
34600             this.animateExpand();
34601         }else{
34602             this.el.show();
34603             if(this.split){
34604                 this.split.el.show();
34605             }
34606             this.collapsedEl.setLocation(-2000,-2000);
34607             this.collapsedEl.hide();
34608             this.fireEvent("invalidated", this);
34609             this.fireEvent("expanded", this);
34610         }
34611     },
34612 */
34613     animateExpand : function(){
34614         // overridden
34615     },
34616
34617     initTabs : function()
34618     {
34619         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
34620         
34621         var ts = new Roo.bootstrap.panel.Tabs({
34622                 el: this.bodyEl.dom,
34623                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
34624                 disableTooltips: this.config.disableTabTips,
34625                 toolbar : this.config.toolbar
34626             });
34627         
34628         if(this.config.hideTabs){
34629             ts.stripWrap.setDisplayed(false);
34630         }
34631         this.tabs = ts;
34632         ts.resizeTabs = this.config.resizeTabs === true;
34633         ts.minTabWidth = this.config.minTabWidth || 40;
34634         ts.maxTabWidth = this.config.maxTabWidth || 250;
34635         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
34636         ts.monitorResize = false;
34637         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
34638         ts.bodyEl.addClass('roo-layout-tabs-body');
34639         this.panels.each(this.initPanelAsTab, this);
34640     },
34641
34642     initPanelAsTab : function(panel){
34643         var ti = this.tabs.addTab(
34644             panel.getEl().id,
34645             panel.getTitle(),
34646             null,
34647             this.config.closeOnTab && panel.isClosable(),
34648             panel.tpl
34649         );
34650         if(panel.tabTip !== undefined){
34651             ti.setTooltip(panel.tabTip);
34652         }
34653         ti.on("activate", function(){
34654               this.setActivePanel(panel);
34655         }, this);
34656         
34657         if(this.config.closeOnTab){
34658             ti.on("beforeclose", function(t, e){
34659                 e.cancel = true;
34660                 this.remove(panel);
34661             }, this);
34662         }
34663         
34664         panel.tabItem = ti;
34665         
34666         return ti;
34667     },
34668
34669     updatePanelTitle : function(panel, title)
34670     {
34671         if(this.activePanel == panel){
34672             this.updateTitle(title);
34673         }
34674         if(this.tabs){
34675             var ti = this.tabs.getTab(panel.getEl().id);
34676             ti.setText(title);
34677             if(panel.tabTip !== undefined){
34678                 ti.setTooltip(panel.tabTip);
34679             }
34680         }
34681     },
34682
34683     updateTitle : function(title){
34684         if(this.titleTextEl && !this.config.title){
34685             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
34686         }
34687     },
34688
34689     setActivePanel : function(panel)
34690     {
34691         panel = this.getPanel(panel);
34692         if(this.activePanel && this.activePanel != panel){
34693             this.activePanel.setActiveState(false);
34694         }
34695         this.activePanel = panel;
34696         panel.setActiveState(true);
34697         if(this.panelSize){
34698             panel.setSize(this.panelSize.width, this.panelSize.height);
34699         }
34700         if(this.closeBtn){
34701             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
34702         }
34703         this.updateTitle(panel.getTitle());
34704         if(this.tabs){
34705             this.fireEvent("invalidated", this);
34706         }
34707         this.fireEvent("panelactivated", this, panel);
34708     },
34709
34710     /**
34711      * Shows the specified panel.
34712      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34713      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34714      */
34715     showPanel : function(panel)
34716     {
34717         panel = this.getPanel(panel);
34718         if(panel){
34719             if(this.tabs){
34720                 var tab = this.tabs.getTab(panel.getEl().id);
34721                 if(tab.isHidden()){
34722                     this.tabs.unhideTab(tab.id);
34723                 }
34724                 tab.activate();
34725             }else{
34726                 this.setActivePanel(panel);
34727             }
34728         }
34729         return panel;
34730     },
34731
34732     /**
34733      * Get the active panel for this region.
34734      * @return {Roo.ContentPanel} The active panel or null
34735      */
34736     getActivePanel : function(){
34737         return this.activePanel;
34738     },
34739
34740     validateVisibility : function(){
34741         if(this.panels.getCount() < 1){
34742             this.updateTitle("&#160;");
34743             this.closeBtn.hide();
34744             this.hide();
34745         }else{
34746             if(!this.isVisible()){
34747                 this.show();
34748             }
34749         }
34750     },
34751
34752     /**
34753      * Adds the passed ContentPanel(s) to this region.
34754      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34755      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34756      */
34757     add : function(panel)
34758     {
34759         if(arguments.length > 1){
34760             for(var i = 0, len = arguments.length; i < len; i++) {
34761                 this.add(arguments[i]);
34762             }
34763             return null;
34764         }
34765         
34766         // if we have not been rendered yet, then we can not really do much of this..
34767         if (!this.bodyEl) {
34768             this.unrendered_panels.push(panel);
34769             return panel;
34770         }
34771         
34772         
34773         
34774         
34775         if(this.hasPanel(panel)){
34776             this.showPanel(panel);
34777             return panel;
34778         }
34779         panel.setRegion(this);
34780         this.panels.add(panel);
34781        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34782             // sinle panel - no tab...?? would it not be better to render it with the tabs,
34783             // and hide them... ???
34784             this.bodyEl.dom.appendChild(panel.getEl().dom);
34785             if(panel.background !== true){
34786                 this.setActivePanel(panel);
34787             }
34788             this.fireEvent("paneladded", this, panel);
34789             return panel;
34790         }
34791         */
34792         if(!this.tabs){
34793             this.initTabs();
34794         }else{
34795             this.initPanelAsTab(panel);
34796         }
34797         
34798         
34799         if(panel.background !== true){
34800             this.tabs.activate(panel.getEl().id);
34801         }
34802         this.fireEvent("paneladded", this, panel);
34803         return panel;
34804     },
34805
34806     /**
34807      * Hides the tab for the specified panel.
34808      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34809      */
34810     hidePanel : function(panel){
34811         if(this.tabs && (panel = this.getPanel(panel))){
34812             this.tabs.hideTab(panel.getEl().id);
34813         }
34814     },
34815
34816     /**
34817      * Unhides the tab for a previously hidden panel.
34818      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34819      */
34820     unhidePanel : function(panel){
34821         if(this.tabs && (panel = this.getPanel(panel))){
34822             this.tabs.unhideTab(panel.getEl().id);
34823         }
34824     },
34825
34826     clearPanels : function(){
34827         while(this.panels.getCount() > 0){
34828              this.remove(this.panels.first());
34829         }
34830     },
34831
34832     /**
34833      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34834      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34835      * @param {Boolean} preservePanel Overrides the config preservePanel option
34836      * @return {Roo.ContentPanel} The panel that was removed
34837      */
34838     remove : function(panel, preservePanel)
34839     {
34840         panel = this.getPanel(panel);
34841         if(!panel){
34842             return null;
34843         }
34844         var e = {};
34845         this.fireEvent("beforeremove", this, panel, e);
34846         if(e.cancel === true){
34847             return null;
34848         }
34849         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34850         var panelId = panel.getId();
34851         this.panels.removeKey(panelId);
34852         if(preservePanel){
34853             document.body.appendChild(panel.getEl().dom);
34854         }
34855         if(this.tabs){
34856             this.tabs.removeTab(panel.getEl().id);
34857         }else if (!preservePanel){
34858             this.bodyEl.dom.removeChild(panel.getEl().dom);
34859         }
34860         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
34861             var p = this.panels.first();
34862             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
34863             tempEl.appendChild(p.getEl().dom);
34864             this.bodyEl.update("");
34865             this.bodyEl.dom.appendChild(p.getEl().dom);
34866             tempEl = null;
34867             this.updateTitle(p.getTitle());
34868             this.tabs = null;
34869             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34870             this.setActivePanel(p);
34871         }
34872         panel.setRegion(null);
34873         if(this.activePanel == panel){
34874             this.activePanel = null;
34875         }
34876         if(this.config.autoDestroy !== false && preservePanel !== true){
34877             try{panel.destroy();}catch(e){}
34878         }
34879         this.fireEvent("panelremoved", this, panel);
34880         return panel;
34881     },
34882
34883     /**
34884      * Returns the TabPanel component used by this region
34885      * @return {Roo.TabPanel}
34886      */
34887     getTabs : function(){
34888         return this.tabs;
34889     },
34890
34891     createTool : function(parentEl, className){
34892         var btn = Roo.DomHelper.append(parentEl, {
34893             tag: "div",
34894             cls: "x-layout-tools-button",
34895             children: [ {
34896                 tag: "div",
34897                 cls: "roo-layout-tools-button-inner " + className,
34898                 html: "&#160;"
34899             }]
34900         }, true);
34901         btn.addClassOnOver("roo-layout-tools-button-over");
34902         return btn;
34903     }
34904 });/*
34905  * Based on:
34906  * Ext JS Library 1.1.1
34907  * Copyright(c) 2006-2007, Ext JS, LLC.
34908  *
34909  * Originally Released Under LGPL - original licence link has changed is not relivant.
34910  *
34911  * Fork - LGPL
34912  * <script type="text/javascript">
34913  */
34914  
34915
34916
34917 /**
34918  * @class Roo.SplitLayoutRegion
34919  * @extends Roo.LayoutRegion
34920  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34921  */
34922 Roo.bootstrap.layout.Split = function(config){
34923     this.cursor = config.cursor;
34924     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
34925 };
34926
34927 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
34928 {
34929     splitTip : "Drag to resize.",
34930     collapsibleSplitTip : "Drag to resize. Double click to hide.",
34931     useSplitTips : false,
34932
34933     applyConfig : function(config){
34934         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
34935     },
34936     
34937     onRender : function(ctr,pos) {
34938         
34939         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
34940         if(!this.config.split){
34941             return;
34942         }
34943         if(!this.split){
34944             
34945             var splitEl = Roo.DomHelper.append(ctr.dom,  {
34946                             tag: "div",
34947                             id: this.el.id + "-split",
34948                             cls: "roo-layout-split roo-layout-split-"+this.position,
34949                             html: "&#160;"
34950             });
34951             /** The SplitBar for this region 
34952             * @type Roo.SplitBar */
34953             // does not exist yet...
34954             Roo.log([this.position, this.orientation]);
34955             
34956             this.split = new Roo.bootstrap.SplitBar({
34957                 dragElement : splitEl,
34958                 resizingElement: this.el,
34959                 orientation : this.orientation
34960             });
34961             
34962             this.split.on("moved", this.onSplitMove, this);
34963             this.split.useShim = this.config.useShim === true;
34964             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34965             if(this.useSplitTips){
34966                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34967             }
34968             //if(config.collapsible){
34969             //    this.split.el.on("dblclick", this.collapse,  this);
34970             //}
34971         }
34972         if(typeof this.config.minSize != "undefined"){
34973             this.split.minSize = this.config.minSize;
34974         }
34975         if(typeof this.config.maxSize != "undefined"){
34976             this.split.maxSize = this.config.maxSize;
34977         }
34978         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
34979             this.hideSplitter();
34980         }
34981         
34982     },
34983
34984     getHMaxSize : function(){
34985          var cmax = this.config.maxSize || 10000;
34986          var center = this.mgr.getRegion("center");
34987          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34988     },
34989
34990     getVMaxSize : function(){
34991          var cmax = this.config.maxSize || 10000;
34992          var center = this.mgr.getRegion("center");
34993          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34994     },
34995
34996     onSplitMove : function(split, newSize){
34997         this.fireEvent("resized", this, newSize);
34998     },
34999     
35000     /** 
35001      * Returns the {@link Roo.SplitBar} for this region.
35002      * @return {Roo.SplitBar}
35003      */
35004     getSplitBar : function(){
35005         return this.split;
35006     },
35007     
35008     hide : function(){
35009         this.hideSplitter();
35010         Roo.bootstrap.layout.Split.superclass.hide.call(this);
35011     },
35012
35013     hideSplitter : function(){
35014         if(this.split){
35015             this.split.el.setLocation(-2000,-2000);
35016             this.split.el.hide();
35017         }
35018     },
35019
35020     show : function(){
35021         if(this.split){
35022             this.split.el.show();
35023         }
35024         Roo.bootstrap.layout.Split.superclass.show.call(this);
35025     },
35026     
35027     beforeSlide: function(){
35028         if(Roo.isGecko){// firefox overflow auto bug workaround
35029             this.bodyEl.clip();
35030             if(this.tabs) {
35031                 this.tabs.bodyEl.clip();
35032             }
35033             if(this.activePanel){
35034                 this.activePanel.getEl().clip();
35035                 
35036                 if(this.activePanel.beforeSlide){
35037                     this.activePanel.beforeSlide();
35038                 }
35039             }
35040         }
35041     },
35042     
35043     afterSlide : function(){
35044         if(Roo.isGecko){// firefox overflow auto bug workaround
35045             this.bodyEl.unclip();
35046             if(this.tabs) {
35047                 this.tabs.bodyEl.unclip();
35048             }
35049             if(this.activePanel){
35050                 this.activePanel.getEl().unclip();
35051                 if(this.activePanel.afterSlide){
35052                     this.activePanel.afterSlide();
35053                 }
35054             }
35055         }
35056     },
35057
35058     initAutoHide : function(){
35059         if(this.autoHide !== false){
35060             if(!this.autoHideHd){
35061                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35062                 this.autoHideHd = {
35063                     "mouseout": function(e){
35064                         if(!e.within(this.el, true)){
35065                             st.delay(500);
35066                         }
35067                     },
35068                     "mouseover" : function(e){
35069                         st.cancel();
35070                     },
35071                     scope : this
35072                 };
35073             }
35074             this.el.on(this.autoHideHd);
35075         }
35076     },
35077
35078     clearAutoHide : function(){
35079         if(this.autoHide !== false){
35080             this.el.un("mouseout", this.autoHideHd.mouseout);
35081             this.el.un("mouseover", this.autoHideHd.mouseover);
35082         }
35083     },
35084
35085     clearMonitor : function(){
35086         Roo.get(document).un("click", this.slideInIf, this);
35087     },
35088
35089     // these names are backwards but not changed for compat
35090     slideOut : function(){
35091         if(this.isSlid || this.el.hasActiveFx()){
35092             return;
35093         }
35094         this.isSlid = true;
35095         if(this.collapseBtn){
35096             this.collapseBtn.hide();
35097         }
35098         this.closeBtnState = this.closeBtn.getStyle('display');
35099         this.closeBtn.hide();
35100         if(this.stickBtn){
35101             this.stickBtn.show();
35102         }
35103         this.el.show();
35104         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35105         this.beforeSlide();
35106         this.el.setStyle("z-index", 10001);
35107         this.el.slideIn(this.getSlideAnchor(), {
35108             callback: function(){
35109                 this.afterSlide();
35110                 this.initAutoHide();
35111                 Roo.get(document).on("click", this.slideInIf, this);
35112                 this.fireEvent("slideshow", this);
35113             },
35114             scope: this,
35115             block: true
35116         });
35117     },
35118
35119     afterSlideIn : function(){
35120         this.clearAutoHide();
35121         this.isSlid = false;
35122         this.clearMonitor();
35123         this.el.setStyle("z-index", "");
35124         if(this.collapseBtn){
35125             this.collapseBtn.show();
35126         }
35127         this.closeBtn.setStyle('display', this.closeBtnState);
35128         if(this.stickBtn){
35129             this.stickBtn.hide();
35130         }
35131         this.fireEvent("slidehide", this);
35132     },
35133
35134     slideIn : function(cb){
35135         if(!this.isSlid || this.el.hasActiveFx()){
35136             Roo.callback(cb);
35137             return;
35138         }
35139         this.isSlid = false;
35140         this.beforeSlide();
35141         this.el.slideOut(this.getSlideAnchor(), {
35142             callback: function(){
35143                 this.el.setLeftTop(-10000, -10000);
35144                 this.afterSlide();
35145                 this.afterSlideIn();
35146                 Roo.callback(cb);
35147             },
35148             scope: this,
35149             block: true
35150         });
35151     },
35152     
35153     slideInIf : function(e){
35154         if(!e.within(this.el)){
35155             this.slideIn();
35156         }
35157     },
35158
35159     animateCollapse : function(){
35160         this.beforeSlide();
35161         this.el.setStyle("z-index", 20000);
35162         var anchor = this.getSlideAnchor();
35163         this.el.slideOut(anchor, {
35164             callback : function(){
35165                 this.el.setStyle("z-index", "");
35166                 this.collapsedEl.slideIn(anchor, {duration:.3});
35167                 this.afterSlide();
35168                 this.el.setLocation(-10000,-10000);
35169                 this.el.hide();
35170                 this.fireEvent("collapsed", this);
35171             },
35172             scope: this,
35173             block: true
35174         });
35175     },
35176
35177     animateExpand : function(){
35178         this.beforeSlide();
35179         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35180         this.el.setStyle("z-index", 20000);
35181         this.collapsedEl.hide({
35182             duration:.1
35183         });
35184         this.el.slideIn(this.getSlideAnchor(), {
35185             callback : function(){
35186                 this.el.setStyle("z-index", "");
35187                 this.afterSlide();
35188                 if(this.split){
35189                     this.split.el.show();
35190                 }
35191                 this.fireEvent("invalidated", this);
35192                 this.fireEvent("expanded", this);
35193             },
35194             scope: this,
35195             block: true
35196         });
35197     },
35198
35199     anchors : {
35200         "west" : "left",
35201         "east" : "right",
35202         "north" : "top",
35203         "south" : "bottom"
35204     },
35205
35206     sanchors : {
35207         "west" : "l",
35208         "east" : "r",
35209         "north" : "t",
35210         "south" : "b"
35211     },
35212
35213     canchors : {
35214         "west" : "tl-tr",
35215         "east" : "tr-tl",
35216         "north" : "tl-bl",
35217         "south" : "bl-tl"
35218     },
35219
35220     getAnchor : function(){
35221         return this.anchors[this.position];
35222     },
35223
35224     getCollapseAnchor : function(){
35225         return this.canchors[this.position];
35226     },
35227
35228     getSlideAnchor : function(){
35229         return this.sanchors[this.position];
35230     },
35231
35232     getAlignAdj : function(){
35233         var cm = this.cmargins;
35234         switch(this.position){
35235             case "west":
35236                 return [0, 0];
35237             break;
35238             case "east":
35239                 return [0, 0];
35240             break;
35241             case "north":
35242                 return [0, 0];
35243             break;
35244             case "south":
35245                 return [0, 0];
35246             break;
35247         }
35248     },
35249
35250     getExpandAdj : function(){
35251         var c = this.collapsedEl, cm = this.cmargins;
35252         switch(this.position){
35253             case "west":
35254                 return [-(cm.right+c.getWidth()+cm.left), 0];
35255             break;
35256             case "east":
35257                 return [cm.right+c.getWidth()+cm.left, 0];
35258             break;
35259             case "north":
35260                 return [0, -(cm.top+cm.bottom+c.getHeight())];
35261             break;
35262             case "south":
35263                 return [0, cm.top+cm.bottom+c.getHeight()];
35264             break;
35265         }
35266     }
35267 });/*
35268  * Based on:
35269  * Ext JS Library 1.1.1
35270  * Copyright(c) 2006-2007, Ext JS, LLC.
35271  *
35272  * Originally Released Under LGPL - original licence link has changed is not relivant.
35273  *
35274  * Fork - LGPL
35275  * <script type="text/javascript">
35276  */
35277 /*
35278  * These classes are private internal classes
35279  */
35280 Roo.bootstrap.layout.Center = function(config){
35281     config.region = "center";
35282     Roo.bootstrap.layout.Region.call(this, config);
35283     this.visible = true;
35284     this.minWidth = config.minWidth || 20;
35285     this.minHeight = config.minHeight || 20;
35286 };
35287
35288 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
35289     hide : function(){
35290         // center panel can't be hidden
35291     },
35292     
35293     show : function(){
35294         // center panel can't be hidden
35295     },
35296     
35297     getMinWidth: function(){
35298         return this.minWidth;
35299     },
35300     
35301     getMinHeight: function(){
35302         return this.minHeight;
35303     }
35304 });
35305
35306
35307
35308
35309  
35310
35311
35312
35313
35314
35315 Roo.bootstrap.layout.North = function(config)
35316 {
35317     config.region = 'north';
35318     config.cursor = 'n-resize';
35319     
35320     Roo.bootstrap.layout.Split.call(this, config);
35321     
35322     
35323     if(this.split){
35324         this.split.placement = Roo.bootstrap.SplitBar.TOP;
35325         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
35326         this.split.el.addClass("roo-layout-split-v");
35327     }
35328     var size = config.initialSize || config.height;
35329     if(typeof size != "undefined"){
35330         this.el.setHeight(size);
35331     }
35332 };
35333 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
35334 {
35335     orientation: Roo.bootstrap.SplitBar.VERTICAL,
35336     
35337     
35338     
35339     getBox : function(){
35340         if(this.collapsed){
35341             return this.collapsedEl.getBox();
35342         }
35343         var box = this.el.getBox();
35344         if(this.split){
35345             box.height += this.split.el.getHeight();
35346         }
35347         return box;
35348     },
35349     
35350     updateBox : function(box){
35351         if(this.split && !this.collapsed){
35352             box.height -= this.split.el.getHeight();
35353             this.split.el.setLeft(box.x);
35354             this.split.el.setTop(box.y+box.height);
35355             this.split.el.setWidth(box.width);
35356         }
35357         if(this.collapsed){
35358             this.updateBody(box.width, null);
35359         }
35360         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35361     }
35362 });
35363
35364
35365
35366
35367
35368 Roo.bootstrap.layout.South = function(config){
35369     config.region = 'south';
35370     config.cursor = 's-resize';
35371     Roo.bootstrap.layout.Split.call(this, config);
35372     if(this.split){
35373         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
35374         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
35375         this.split.el.addClass("roo-layout-split-v");
35376     }
35377     var size = config.initialSize || config.height;
35378     if(typeof size != "undefined"){
35379         this.el.setHeight(size);
35380     }
35381 };
35382
35383 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
35384     orientation: Roo.bootstrap.SplitBar.VERTICAL,
35385     getBox : function(){
35386         if(this.collapsed){
35387             return this.collapsedEl.getBox();
35388         }
35389         var box = this.el.getBox();
35390         if(this.split){
35391             var sh = this.split.el.getHeight();
35392             box.height += sh;
35393             box.y -= sh;
35394         }
35395         return box;
35396     },
35397     
35398     updateBox : function(box){
35399         if(this.split && !this.collapsed){
35400             var sh = this.split.el.getHeight();
35401             box.height -= sh;
35402             box.y += sh;
35403             this.split.el.setLeft(box.x);
35404             this.split.el.setTop(box.y-sh);
35405             this.split.el.setWidth(box.width);
35406         }
35407         if(this.collapsed){
35408             this.updateBody(box.width, null);
35409         }
35410         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35411     }
35412 });
35413
35414 Roo.bootstrap.layout.East = function(config){
35415     config.region = "east";
35416     config.cursor = "e-resize";
35417     Roo.bootstrap.layout.Split.call(this, config);
35418     if(this.split){
35419         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
35420         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
35421         this.split.el.addClass("roo-layout-split-h");
35422     }
35423     var size = config.initialSize || config.width;
35424     if(typeof size != "undefined"){
35425         this.el.setWidth(size);
35426     }
35427 };
35428 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
35429     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
35430     getBox : function(){
35431         if(this.collapsed){
35432             return this.collapsedEl.getBox();
35433         }
35434         var box = this.el.getBox();
35435         if(this.split){
35436             var sw = this.split.el.getWidth();
35437             box.width += sw;
35438             box.x -= sw;
35439         }
35440         return box;
35441     },
35442
35443     updateBox : function(box){
35444         if(this.split && !this.collapsed){
35445             var sw = this.split.el.getWidth();
35446             box.width -= sw;
35447             this.split.el.setLeft(box.x);
35448             this.split.el.setTop(box.y);
35449             this.split.el.setHeight(box.height);
35450             box.x += sw;
35451         }
35452         if(this.collapsed){
35453             this.updateBody(null, box.height);
35454         }
35455         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35456     }
35457 });
35458
35459 Roo.bootstrap.layout.West = function(config){
35460     config.region = "west";
35461     config.cursor = "w-resize";
35462     
35463     Roo.bootstrap.layout.Split.call(this, config);
35464     if(this.split){
35465         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
35466         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
35467         this.split.el.addClass("roo-layout-split-h");
35468     }
35469     
35470 };
35471 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
35472     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
35473     
35474     onRender: function(ctr, pos)
35475     {
35476         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
35477         var size = this.config.initialSize || this.config.width;
35478         if(typeof size != "undefined"){
35479             this.el.setWidth(size);
35480         }
35481     },
35482     
35483     getBox : function(){
35484         if(this.collapsed){
35485             return this.collapsedEl.getBox();
35486         }
35487         var box = this.el.getBox();
35488         if(this.split){
35489             box.width += this.split.el.getWidth();
35490         }
35491         return box;
35492     },
35493     
35494     updateBox : function(box){
35495         if(this.split && !this.collapsed){
35496             var sw = this.split.el.getWidth();
35497             box.width -= sw;
35498             this.split.el.setLeft(box.x+box.width);
35499             this.split.el.setTop(box.y);
35500             this.split.el.setHeight(box.height);
35501         }
35502         if(this.collapsed){
35503             this.updateBody(null, box.height);
35504         }
35505         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35506     }
35507 });
35508 Roo.namespace("Roo.bootstrap.panel");/*
35509  * Based on:
35510  * Ext JS Library 1.1.1
35511  * Copyright(c) 2006-2007, Ext JS, LLC.
35512  *
35513  * Originally Released Under LGPL - original licence link has changed is not relivant.
35514  *
35515  * Fork - LGPL
35516  * <script type="text/javascript">
35517  */
35518 /**
35519  * @class Roo.ContentPanel
35520  * @extends Roo.util.Observable
35521  * A basic ContentPanel element.
35522  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
35523  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
35524  * @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
35525  * @cfg {Boolean}   closable      True if the panel can be closed/removed
35526  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
35527  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
35528  * @cfg {Toolbar}   toolbar       A toolbar for this panel
35529  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
35530  * @cfg {String} title          The title for this panel
35531  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
35532  * @cfg {String} url            Calls {@link #setUrl} with this value
35533  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
35534  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
35535  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
35536  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
35537  * @cfg {Boolean} badges render the badges
35538
35539  * @constructor
35540  * Create a new ContentPanel.
35541  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
35542  * @param {String/Object} config A string to set only the title or a config object
35543  * @param {String} content (optional) Set the HTML content for this panel
35544  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
35545  */
35546 Roo.bootstrap.panel.Content = function( config){
35547     
35548     this.tpl = config.tpl || false;
35549     
35550     var el = config.el;
35551     var content = config.content;
35552
35553     if(config.autoCreate){ // xtype is available if this is called from factory
35554         el = Roo.id();
35555     }
35556     this.el = Roo.get(el);
35557     if(!this.el && config && config.autoCreate){
35558         if(typeof config.autoCreate == "object"){
35559             if(!config.autoCreate.id){
35560                 config.autoCreate.id = config.id||el;
35561             }
35562             this.el = Roo.DomHelper.append(document.body,
35563                         config.autoCreate, true);
35564         }else{
35565             var elcfg =  {   tag: "div",
35566                             cls: "roo-layout-inactive-content",
35567                             id: config.id||el
35568                             };
35569             if (config.html) {
35570                 elcfg.html = config.html;
35571                 
35572             }
35573                         
35574             this.el = Roo.DomHelper.append(document.body, elcfg , true);
35575         }
35576     } 
35577     this.closable = false;
35578     this.loaded = false;
35579     this.active = false;
35580    
35581       
35582     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
35583         
35584         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
35585         
35586         this.wrapEl = this.el; //this.el.wrap();
35587         var ti = [];
35588         if (config.toolbar.items) {
35589             ti = config.toolbar.items ;
35590             delete config.toolbar.items ;
35591         }
35592         
35593         var nitems = [];
35594         this.toolbar.render(this.wrapEl, 'before');
35595         for(var i =0;i < ti.length;i++) {
35596           //  Roo.log(['add child', items[i]]);
35597             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
35598         }
35599         this.toolbar.items = nitems;
35600         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
35601         delete config.toolbar;
35602         
35603     }
35604     /*
35605     // xtype created footer. - not sure if will work as we normally have to render first..
35606     if (this.footer && !this.footer.el && this.footer.xtype) {
35607         if (!this.wrapEl) {
35608             this.wrapEl = this.el.wrap();
35609         }
35610     
35611         this.footer.container = this.wrapEl.createChild();
35612          
35613         this.footer = Roo.factory(this.footer, Roo);
35614         
35615     }
35616     */
35617     
35618      if(typeof config == "string"){
35619         this.title = config;
35620     }else{
35621         Roo.apply(this, config);
35622     }
35623     
35624     if(this.resizeEl){
35625         this.resizeEl = Roo.get(this.resizeEl, true);
35626     }else{
35627         this.resizeEl = this.el;
35628     }
35629     // handle view.xtype
35630     
35631  
35632     
35633     
35634     this.addEvents({
35635         /**
35636          * @event activate
35637          * Fires when this panel is activated. 
35638          * @param {Roo.ContentPanel} this
35639          */
35640         "activate" : true,
35641         /**
35642          * @event deactivate
35643          * Fires when this panel is activated. 
35644          * @param {Roo.ContentPanel} this
35645          */
35646         "deactivate" : true,
35647
35648         /**
35649          * @event resize
35650          * Fires when this panel is resized if fitToFrame is true.
35651          * @param {Roo.ContentPanel} this
35652          * @param {Number} width The width after any component adjustments
35653          * @param {Number} height The height after any component adjustments
35654          */
35655         "resize" : true,
35656         
35657          /**
35658          * @event render
35659          * Fires when this tab is created
35660          * @param {Roo.ContentPanel} this
35661          */
35662         "render" : true
35663         
35664         
35665         
35666     });
35667     
35668
35669     
35670     
35671     if(this.autoScroll){
35672         this.resizeEl.setStyle("overflow", "auto");
35673     } else {
35674         // fix randome scrolling
35675         //this.el.on('scroll', function() {
35676         //    Roo.log('fix random scolling');
35677         //    this.scrollTo('top',0); 
35678         //});
35679     }
35680     content = content || this.content;
35681     if(content){
35682         this.setContent(content);
35683     }
35684     if(config && config.url){
35685         this.setUrl(this.url, this.params, this.loadOnce);
35686     }
35687     
35688     
35689     
35690     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
35691     
35692     if (this.view && typeof(this.view.xtype) != 'undefined') {
35693         this.view.el = this.el.appendChild(document.createElement("div"));
35694         this.view = Roo.factory(this.view); 
35695         this.view.render  &&  this.view.render(false, '');  
35696     }
35697     
35698     
35699     this.fireEvent('render', this);
35700 };
35701
35702 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
35703     
35704     tabTip : '',
35705     
35706     setRegion : function(region){
35707         this.region = region;
35708         this.setActiveClass(region && !this.background);
35709     },
35710     
35711     
35712     setActiveClass: function(state)
35713     {
35714         if(state){
35715            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
35716            this.el.setStyle('position','relative');
35717         }else{
35718            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
35719            this.el.setStyle('position', 'absolute');
35720         } 
35721     },
35722     
35723     /**
35724      * Returns the toolbar for this Panel if one was configured. 
35725      * @return {Roo.Toolbar} 
35726      */
35727     getToolbar : function(){
35728         return this.toolbar;
35729     },
35730     
35731     setActiveState : function(active)
35732     {
35733         this.active = active;
35734         this.setActiveClass(active);
35735         if(!active){
35736             this.fireEvent("deactivate", this);
35737         }else{
35738             this.fireEvent("activate", this);
35739         }
35740     },
35741     /**
35742      * Updates this panel's element
35743      * @param {String} content The new content
35744      * @param {Boolean} loadScripts (optional) true to look for and process scripts
35745     */
35746     setContent : function(content, loadScripts){
35747         this.el.update(content, loadScripts);
35748     },
35749
35750     ignoreResize : function(w, h){
35751         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35752             return true;
35753         }else{
35754             this.lastSize = {width: w, height: h};
35755             return false;
35756         }
35757     },
35758     /**
35759      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35760      * @return {Roo.UpdateManager} The UpdateManager
35761      */
35762     getUpdateManager : function(){
35763         return this.el.getUpdateManager();
35764     },
35765      /**
35766      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35767      * @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:
35768 <pre><code>
35769 panel.load({
35770     url: "your-url.php",
35771     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35772     callback: yourFunction,
35773     scope: yourObject, //(optional scope)
35774     discardUrl: false,
35775     nocache: false,
35776     text: "Loading...",
35777     timeout: 30,
35778     scripts: false
35779 });
35780 </code></pre>
35781      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35782      * 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.
35783      * @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}
35784      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35785      * @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.
35786      * @return {Roo.ContentPanel} this
35787      */
35788     load : function(){
35789         var um = this.el.getUpdateManager();
35790         um.update.apply(um, arguments);
35791         return this;
35792     },
35793
35794
35795     /**
35796      * 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.
35797      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35798      * @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)
35799      * @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)
35800      * @return {Roo.UpdateManager} The UpdateManager
35801      */
35802     setUrl : function(url, params, loadOnce){
35803         if(this.refreshDelegate){
35804             this.removeListener("activate", this.refreshDelegate);
35805         }
35806         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35807         this.on("activate", this.refreshDelegate);
35808         return this.el.getUpdateManager();
35809     },
35810     
35811     _handleRefresh : function(url, params, loadOnce){
35812         if(!loadOnce || !this.loaded){
35813             var updater = this.el.getUpdateManager();
35814             updater.update(url, params, this._setLoaded.createDelegate(this));
35815         }
35816     },
35817     
35818     _setLoaded : function(){
35819         this.loaded = true;
35820     }, 
35821     
35822     /**
35823      * Returns this panel's id
35824      * @return {String} 
35825      */
35826     getId : function(){
35827         return this.el.id;
35828     },
35829     
35830     /** 
35831      * Returns this panel's element - used by regiosn to add.
35832      * @return {Roo.Element} 
35833      */
35834     getEl : function(){
35835         return this.wrapEl || this.el;
35836     },
35837     
35838    
35839     
35840     adjustForComponents : function(width, height)
35841     {
35842         //Roo.log('adjustForComponents ');
35843         if(this.resizeEl != this.el){
35844             width -= this.el.getFrameWidth('lr');
35845             height -= this.el.getFrameWidth('tb');
35846         }
35847         if(this.toolbar){
35848             var te = this.toolbar.getEl();
35849             te.setWidth(width);
35850             height -= te.getHeight();
35851         }
35852         if(this.footer){
35853             var te = this.footer.getEl();
35854             te.setWidth(width);
35855             height -= te.getHeight();
35856         }
35857         
35858         
35859         if(this.adjustments){
35860             width += this.adjustments[0];
35861             height += this.adjustments[1];
35862         }
35863         return {"width": width, "height": height};
35864     },
35865     
35866     setSize : function(width, height){
35867         if(this.fitToFrame && !this.ignoreResize(width, height)){
35868             if(this.fitContainer && this.resizeEl != this.el){
35869                 this.el.setSize(width, height);
35870             }
35871             var size = this.adjustForComponents(width, height);
35872             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
35873             this.fireEvent('resize', this, size.width, size.height);
35874         }
35875     },
35876     
35877     /**
35878      * Returns this panel's title
35879      * @return {String} 
35880      */
35881     getTitle : function(){
35882         
35883         if (typeof(this.title) != 'object') {
35884             return this.title;
35885         }
35886         
35887         var t = '';
35888         for (var k in this.title) {
35889             if (!this.title.hasOwnProperty(k)) {
35890                 continue;
35891             }
35892             
35893             if (k.indexOf('-') >= 0) {
35894                 var s = k.split('-');
35895                 for (var i = 0; i<s.length; i++) {
35896                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
35897                 }
35898             } else {
35899                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
35900             }
35901         }
35902         return t;
35903     },
35904     
35905     /**
35906      * Set this panel's title
35907      * @param {String} title
35908      */
35909     setTitle : function(title){
35910         this.title = title;
35911         if(this.region){
35912             this.region.updatePanelTitle(this, title);
35913         }
35914     },
35915     
35916     /**
35917      * Returns true is this panel was configured to be closable
35918      * @return {Boolean} 
35919      */
35920     isClosable : function(){
35921         return this.closable;
35922     },
35923     
35924     beforeSlide : function(){
35925         this.el.clip();
35926         this.resizeEl.clip();
35927     },
35928     
35929     afterSlide : function(){
35930         this.el.unclip();
35931         this.resizeEl.unclip();
35932     },
35933     
35934     /**
35935      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
35936      *   Will fail silently if the {@link #setUrl} method has not been called.
35937      *   This does not activate the panel, just updates its content.
35938      */
35939     refresh : function(){
35940         if(this.refreshDelegate){
35941            this.loaded = false;
35942            this.refreshDelegate();
35943         }
35944     },
35945     
35946     /**
35947      * Destroys this panel
35948      */
35949     destroy : function(){
35950         this.el.removeAllListeners();
35951         var tempEl = document.createElement("span");
35952         tempEl.appendChild(this.el.dom);
35953         tempEl.innerHTML = "";
35954         this.el.remove();
35955         this.el = null;
35956     },
35957     
35958     /**
35959      * form - if the content panel contains a form - this is a reference to it.
35960      * @type {Roo.form.Form}
35961      */
35962     form : false,
35963     /**
35964      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35965      *    This contains a reference to it.
35966      * @type {Roo.View}
35967      */
35968     view : false,
35969     
35970       /**
35971      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35972      * <pre><code>
35973
35974 layout.addxtype({
35975        xtype : 'Form',
35976        items: [ .... ]
35977    }
35978 );
35979
35980 </code></pre>
35981      * @param {Object} cfg Xtype definition of item to add.
35982      */
35983     
35984     
35985     getChildContainer: function () {
35986         return this.getEl();
35987     }
35988     
35989     
35990     /*
35991         var  ret = new Roo.factory(cfg);
35992         return ret;
35993         
35994         
35995         // add form..
35996         if (cfg.xtype.match(/^Form$/)) {
35997             
35998             var el;
35999             //if (this.footer) {
36000             //    el = this.footer.container.insertSibling(false, 'before');
36001             //} else {
36002                 el = this.el.createChild();
36003             //}
36004
36005             this.form = new  Roo.form.Form(cfg);
36006             
36007             
36008             if ( this.form.allItems.length) {
36009                 this.form.render(el.dom);
36010             }
36011             return this.form;
36012         }
36013         // should only have one of theses..
36014         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36015             // views.. should not be just added - used named prop 'view''
36016             
36017             cfg.el = this.el.appendChild(document.createElement("div"));
36018             // factory?
36019             
36020             var ret = new Roo.factory(cfg);
36021              
36022              ret.render && ret.render(false, ''); // render blank..
36023             this.view = ret;
36024             return ret;
36025         }
36026         return false;
36027     }
36028     \*/
36029 });
36030  
36031 /**
36032  * @class Roo.bootstrap.panel.Grid
36033  * @extends Roo.bootstrap.panel.Content
36034  * @constructor
36035  * Create a new GridPanel.
36036  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36037  * @param {Object} config A the config object
36038   
36039  */
36040
36041
36042
36043 Roo.bootstrap.panel.Grid = function(config)
36044 {
36045     
36046       
36047     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36048         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36049
36050     config.el = this.wrapper;
36051     //this.el = this.wrapper;
36052     
36053       if (config.container) {
36054         // ctor'ed from a Border/panel.grid
36055         
36056         
36057         this.wrapper.setStyle("overflow", "hidden");
36058         this.wrapper.addClass('roo-grid-container');
36059
36060     }
36061     
36062     
36063     if(config.toolbar){
36064         var tool_el = this.wrapper.createChild();    
36065         this.toolbar = Roo.factory(config.toolbar);
36066         var ti = [];
36067         if (config.toolbar.items) {
36068             ti = config.toolbar.items ;
36069             delete config.toolbar.items ;
36070         }
36071         
36072         var nitems = [];
36073         this.toolbar.render(tool_el);
36074         for(var i =0;i < ti.length;i++) {
36075           //  Roo.log(['add child', items[i]]);
36076             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36077         }
36078         this.toolbar.items = nitems;
36079         
36080         delete config.toolbar;
36081     }
36082     
36083     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36084     config.grid.scrollBody = true;;
36085     config.grid.monitorWindowResize = false; // turn off autosizing
36086     config.grid.autoHeight = false;
36087     config.grid.autoWidth = false;
36088     
36089     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36090     
36091     if (config.background) {
36092         // render grid on panel activation (if panel background)
36093         this.on('activate', function(gp) {
36094             if (!gp.grid.rendered) {
36095                 gp.grid.render(this.wrapper);
36096                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
36097             }
36098         });
36099             
36100     } else {
36101         this.grid.render(this.wrapper);
36102         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
36103
36104     }
36105     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36106     // ??? needed ??? config.el = this.wrapper;
36107     
36108     
36109     
36110   
36111     // xtype created footer. - not sure if will work as we normally have to render first..
36112     if (this.footer && !this.footer.el && this.footer.xtype) {
36113         
36114         var ctr = this.grid.getView().getFooterPanel(true);
36115         this.footer.dataSource = this.grid.dataSource;
36116         this.footer = Roo.factory(this.footer, Roo);
36117         this.footer.render(ctr);
36118         
36119     }
36120     
36121     
36122     
36123     
36124      
36125 };
36126
36127 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
36128     getId : function(){
36129         return this.grid.id;
36130     },
36131     
36132     /**
36133      * Returns the grid for this panel
36134      * @return {Roo.bootstrap.Table} 
36135      */
36136     getGrid : function(){
36137         return this.grid;    
36138     },
36139     
36140     setSize : function(width, height){
36141         if(!this.ignoreResize(width, height)){
36142             var grid = this.grid;
36143             var size = this.adjustForComponents(width, height);
36144             var gridel = grid.getGridEl();
36145             gridel.setSize(size.width, size.height);
36146             /*
36147             var thd = grid.getGridEl().select('thead',true).first();
36148             var tbd = grid.getGridEl().select('tbody', true).first();
36149             if (tbd) {
36150                 tbd.setSize(width, height - thd.getHeight());
36151             }
36152             */
36153             grid.autoSize();
36154         }
36155     },
36156      
36157     
36158     
36159     beforeSlide : function(){
36160         this.grid.getView().scroller.clip();
36161     },
36162     
36163     afterSlide : function(){
36164         this.grid.getView().scroller.unclip();
36165     },
36166     
36167     destroy : function(){
36168         this.grid.destroy();
36169         delete this.grid;
36170         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
36171     }
36172 });
36173
36174 /**
36175  * @class Roo.bootstrap.panel.Nest
36176  * @extends Roo.bootstrap.panel.Content
36177  * @constructor
36178  * Create a new Panel, that can contain a layout.Border.
36179  * 
36180  * 
36181  * @param {Roo.BorderLayout} layout The layout for this panel
36182  * @param {String/Object} config A string to set only the title or a config object
36183  */
36184 Roo.bootstrap.panel.Nest = function(config)
36185 {
36186     // construct with only one argument..
36187     /* FIXME - implement nicer consturctors
36188     if (layout.layout) {
36189         config = layout;
36190         layout = config.layout;
36191         delete config.layout;
36192     }
36193     if (layout.xtype && !layout.getEl) {
36194         // then layout needs constructing..
36195         layout = Roo.factory(layout, Roo);
36196     }
36197     */
36198     
36199     config.el =  config.layout.getEl();
36200     
36201     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
36202     
36203     config.layout.monitorWindowResize = false; // turn off autosizing
36204     this.layout = config.layout;
36205     this.layout.getEl().addClass("roo-layout-nested-layout");
36206     
36207     
36208     
36209     
36210 };
36211
36212 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
36213
36214     setSize : function(width, height){
36215         if(!this.ignoreResize(width, height)){
36216             var size = this.adjustForComponents(width, height);
36217             var el = this.layout.getEl();
36218             if (size.height < 1) {
36219                 el.setWidth(size.width);   
36220             } else {
36221                 el.setSize(size.width, size.height);
36222             }
36223             var touch = el.dom.offsetWidth;
36224             this.layout.layout();
36225             // ie requires a double layout on the first pass
36226             if(Roo.isIE && !this.initialized){
36227                 this.initialized = true;
36228                 this.layout.layout();
36229             }
36230         }
36231     },
36232     
36233     // activate all subpanels if not currently active..
36234     
36235     setActiveState : function(active){
36236         this.active = active;
36237         this.setActiveClass(active);
36238         
36239         if(!active){
36240             this.fireEvent("deactivate", this);
36241             return;
36242         }
36243         
36244         this.fireEvent("activate", this);
36245         // not sure if this should happen before or after..
36246         if (!this.layout) {
36247             return; // should not happen..
36248         }
36249         var reg = false;
36250         for (var r in this.layout.regions) {
36251             reg = this.layout.getRegion(r);
36252             if (reg.getActivePanel()) {
36253                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
36254                 reg.setActivePanel(reg.getActivePanel());
36255                 continue;
36256             }
36257             if (!reg.panels.length) {
36258                 continue;
36259             }
36260             reg.showPanel(reg.getPanel(0));
36261         }
36262         
36263         
36264         
36265         
36266     },
36267     
36268     /**
36269      * Returns the nested BorderLayout for this panel
36270      * @return {Roo.BorderLayout} 
36271      */
36272     getLayout : function(){
36273         return this.layout;
36274     },
36275     
36276      /**
36277      * Adds a xtype elements to the layout of the nested panel
36278      * <pre><code>
36279
36280 panel.addxtype({
36281        xtype : 'ContentPanel',
36282        region: 'west',
36283        items: [ .... ]
36284    }
36285 );
36286
36287 panel.addxtype({
36288         xtype : 'NestedLayoutPanel',
36289         region: 'west',
36290         layout: {
36291            center: { },
36292            west: { }   
36293         },
36294         items : [ ... list of content panels or nested layout panels.. ]
36295    }
36296 );
36297 </code></pre>
36298      * @param {Object} cfg Xtype definition of item to add.
36299      */
36300     addxtype : function(cfg) {
36301         return this.layout.addxtype(cfg);
36302     
36303     }
36304 });        /*
36305  * Based on:
36306  * Ext JS Library 1.1.1
36307  * Copyright(c) 2006-2007, Ext JS, LLC.
36308  *
36309  * Originally Released Under LGPL - original licence link has changed is not relivant.
36310  *
36311  * Fork - LGPL
36312  * <script type="text/javascript">
36313  */
36314 /**
36315  * @class Roo.TabPanel
36316  * @extends Roo.util.Observable
36317  * A lightweight tab container.
36318  * <br><br>
36319  * Usage:
36320  * <pre><code>
36321 // basic tabs 1, built from existing content
36322 var tabs = new Roo.TabPanel("tabs1");
36323 tabs.addTab("script", "View Script");
36324 tabs.addTab("markup", "View Markup");
36325 tabs.activate("script");
36326
36327 // more advanced tabs, built from javascript
36328 var jtabs = new Roo.TabPanel("jtabs");
36329 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
36330
36331 // set up the UpdateManager
36332 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
36333 var updater = tab2.getUpdateManager();
36334 updater.setDefaultUrl("ajax1.htm");
36335 tab2.on('activate', updater.refresh, updater, true);
36336
36337 // Use setUrl for Ajax loading
36338 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
36339 tab3.setUrl("ajax2.htm", null, true);
36340
36341 // Disabled tab
36342 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
36343 tab4.disable();
36344
36345 jtabs.activate("jtabs-1");
36346  * </code></pre>
36347  * @constructor
36348  * Create a new TabPanel.
36349  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
36350  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
36351  */
36352 Roo.bootstrap.panel.Tabs = function(config){
36353     /**
36354     * The container element for this TabPanel.
36355     * @type Roo.Element
36356     */
36357     this.el = Roo.get(config.el);
36358     delete config.el;
36359     if(config){
36360         if(typeof config == "boolean"){
36361             this.tabPosition = config ? "bottom" : "top";
36362         }else{
36363             Roo.apply(this, config);
36364         }
36365     }
36366     
36367     if(this.tabPosition == "bottom"){
36368         this.bodyEl = Roo.get(this.createBody(this.el.dom));
36369         this.el.addClass("roo-tabs-bottom");
36370     }
36371     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
36372     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
36373     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
36374     if(Roo.isIE){
36375         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
36376     }
36377     if(this.tabPosition != "bottom"){
36378         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
36379          * @type Roo.Element
36380          */
36381         this.bodyEl = Roo.get(this.createBody(this.el.dom));
36382         this.el.addClass("roo-tabs-top");
36383     }
36384     this.items = [];
36385
36386     this.bodyEl.setStyle("position", "relative");
36387
36388     this.active = null;
36389     this.activateDelegate = this.activate.createDelegate(this);
36390
36391     this.addEvents({
36392         /**
36393          * @event tabchange
36394          * Fires when the active tab changes
36395          * @param {Roo.TabPanel} this
36396          * @param {Roo.TabPanelItem} activePanel The new active tab
36397          */
36398         "tabchange": true,
36399         /**
36400          * @event beforetabchange
36401          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
36402          * @param {Roo.TabPanel} this
36403          * @param {Object} e Set cancel to true on this object to cancel the tab change
36404          * @param {Roo.TabPanelItem} tab The tab being changed to
36405          */
36406         "beforetabchange" : true
36407     });
36408
36409     Roo.EventManager.onWindowResize(this.onResize, this);
36410     this.cpad = this.el.getPadding("lr");
36411     this.hiddenCount = 0;
36412
36413
36414     // toolbar on the tabbar support...
36415     if (this.toolbar) {
36416         alert("no toolbar support yet");
36417         this.toolbar  = false;
36418         /*
36419         var tcfg = this.toolbar;
36420         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
36421         this.toolbar = new Roo.Toolbar(tcfg);
36422         if (Roo.isSafari) {
36423             var tbl = tcfg.container.child('table', true);
36424             tbl.setAttribute('width', '100%');
36425         }
36426         */
36427         
36428     }
36429    
36430
36431
36432     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
36433 };
36434
36435 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
36436     /*
36437      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
36438      */
36439     tabPosition : "top",
36440     /*
36441      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
36442      */
36443     currentTabWidth : 0,
36444     /*
36445      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
36446      */
36447     minTabWidth : 40,
36448     /*
36449      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
36450      */
36451     maxTabWidth : 250,
36452     /*
36453      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
36454      */
36455     preferredTabWidth : 175,
36456     /*
36457      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
36458      */
36459     resizeTabs : false,
36460     /*
36461      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
36462      */
36463     monitorResize : true,
36464     /*
36465      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
36466      */
36467     toolbar : false,
36468
36469     /**
36470      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
36471      * @param {String} id The id of the div to use <b>or create</b>
36472      * @param {String} text The text for the tab
36473      * @param {String} content (optional) Content to put in the TabPanelItem body
36474      * @param {Boolean} closable (optional) True to create a close icon on the tab
36475      * @return {Roo.TabPanelItem} The created TabPanelItem
36476      */
36477     addTab : function(id, text, content, closable, tpl)
36478     {
36479         var item = new Roo.bootstrap.panel.TabItem({
36480             panel: this,
36481             id : id,
36482             text : text,
36483             closable : closable,
36484             tpl : tpl
36485         });
36486         this.addTabItem(item);
36487         if(content){
36488             item.setContent(content);
36489         }
36490         return item;
36491     },
36492
36493     /**
36494      * Returns the {@link Roo.TabPanelItem} with the specified id/index
36495      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
36496      * @return {Roo.TabPanelItem}
36497      */
36498     getTab : function(id){
36499         return this.items[id];
36500     },
36501
36502     /**
36503      * Hides the {@link Roo.TabPanelItem} with the specified id/index
36504      * @param {String/Number} id The id or index of the TabPanelItem to hide.
36505      */
36506     hideTab : function(id){
36507         var t = this.items[id];
36508         if(!t.isHidden()){
36509            t.setHidden(true);
36510            this.hiddenCount++;
36511            this.autoSizeTabs();
36512         }
36513     },
36514
36515     /**
36516      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
36517      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
36518      */
36519     unhideTab : function(id){
36520         var t = this.items[id];
36521         if(t.isHidden()){
36522            t.setHidden(false);
36523            this.hiddenCount--;
36524            this.autoSizeTabs();
36525         }
36526     },
36527
36528     /**
36529      * Adds an existing {@link Roo.TabPanelItem}.
36530      * @param {Roo.TabPanelItem} item The TabPanelItem to add
36531      */
36532     addTabItem : function(item){
36533         this.items[item.id] = item;
36534         this.items.push(item);
36535       //  if(this.resizeTabs){
36536     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
36537   //         this.autoSizeTabs();
36538 //        }else{
36539 //            item.autoSize();
36540        // }
36541     },
36542
36543     /**
36544      * Removes a {@link Roo.TabPanelItem}.
36545      * @param {String/Number} id The id or index of the TabPanelItem to remove.
36546      */
36547     removeTab : function(id){
36548         var items = this.items;
36549         var tab = items[id];
36550         if(!tab) { return; }
36551         var index = items.indexOf(tab);
36552         if(this.active == tab && items.length > 1){
36553             var newTab = this.getNextAvailable(index);
36554             if(newTab) {
36555                 newTab.activate();
36556             }
36557         }
36558         this.stripEl.dom.removeChild(tab.pnode.dom);
36559         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
36560             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
36561         }
36562         items.splice(index, 1);
36563         delete this.items[tab.id];
36564         tab.fireEvent("close", tab);
36565         tab.purgeListeners();
36566         this.autoSizeTabs();
36567     },
36568
36569     getNextAvailable : function(start){
36570         var items = this.items;
36571         var index = start;
36572         // look for a next tab that will slide over to
36573         // replace the one being removed
36574         while(index < items.length){
36575             var item = items[++index];
36576             if(item && !item.isHidden()){
36577                 return item;
36578             }
36579         }
36580         // if one isn't found select the previous tab (on the left)
36581         index = start;
36582         while(index >= 0){
36583             var item = items[--index];
36584             if(item && !item.isHidden()){
36585                 return item;
36586             }
36587         }
36588         return null;
36589     },
36590
36591     /**
36592      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
36593      * @param {String/Number} id The id or index of the TabPanelItem to disable.
36594      */
36595     disableTab : function(id){
36596         var tab = this.items[id];
36597         if(tab && this.active != tab){
36598             tab.disable();
36599         }
36600     },
36601
36602     /**
36603      * Enables a {@link Roo.TabPanelItem} that is disabled.
36604      * @param {String/Number} id The id or index of the TabPanelItem to enable.
36605      */
36606     enableTab : function(id){
36607         var tab = this.items[id];
36608         tab.enable();
36609     },
36610
36611     /**
36612      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
36613      * @param {String/Number} id The id or index of the TabPanelItem to activate.
36614      * @return {Roo.TabPanelItem} The TabPanelItem.
36615      */
36616     activate : function(id){
36617         var tab = this.items[id];
36618         if(!tab){
36619             return null;
36620         }
36621         if(tab == this.active || tab.disabled){
36622             return tab;
36623         }
36624         var e = {};
36625         this.fireEvent("beforetabchange", this, e, tab);
36626         if(e.cancel !== true && !tab.disabled){
36627             if(this.active){
36628                 this.active.hide();
36629             }
36630             this.active = this.items[id];
36631             this.active.show();
36632             this.fireEvent("tabchange", this, this.active);
36633         }
36634         return tab;
36635     },
36636
36637     /**
36638      * Gets the active {@link Roo.TabPanelItem}.
36639      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
36640      */
36641     getActiveTab : function(){
36642         return this.active;
36643     },
36644
36645     /**
36646      * Updates the tab body element to fit the height of the container element
36647      * for overflow scrolling
36648      * @param {Number} targetHeight (optional) Override the starting height from the elements height
36649      */
36650     syncHeight : function(targetHeight){
36651         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36652         var bm = this.bodyEl.getMargins();
36653         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
36654         this.bodyEl.setHeight(newHeight);
36655         return newHeight;
36656     },
36657
36658     onResize : function(){
36659         if(this.monitorResize){
36660             this.autoSizeTabs();
36661         }
36662     },
36663
36664     /**
36665      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
36666      */
36667     beginUpdate : function(){
36668         this.updating = true;
36669     },
36670
36671     /**
36672      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
36673      */
36674     endUpdate : function(){
36675         this.updating = false;
36676         this.autoSizeTabs();
36677     },
36678
36679     /**
36680      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
36681      */
36682     autoSizeTabs : function(){
36683         var count = this.items.length;
36684         var vcount = count - this.hiddenCount;
36685         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
36686             return;
36687         }
36688         var w = Math.max(this.el.getWidth() - this.cpad, 10);
36689         var availWidth = Math.floor(w / vcount);
36690         var b = this.stripBody;
36691         if(b.getWidth() > w){
36692             var tabs = this.items;
36693             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
36694             if(availWidth < this.minTabWidth){
36695                 /*if(!this.sleft){    // incomplete scrolling code
36696                     this.createScrollButtons();
36697                 }
36698                 this.showScroll();
36699                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
36700             }
36701         }else{
36702             if(this.currentTabWidth < this.preferredTabWidth){
36703                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
36704             }
36705         }
36706     },
36707
36708     /**
36709      * Returns the number of tabs in this TabPanel.
36710      * @return {Number}
36711      */
36712      getCount : function(){
36713          return this.items.length;
36714      },
36715
36716     /**
36717      * Resizes all the tabs to the passed width
36718      * @param {Number} The new width
36719      */
36720     setTabWidth : function(width){
36721         this.currentTabWidth = width;
36722         for(var i = 0, len = this.items.length; i < len; i++) {
36723                 if(!this.items[i].isHidden()) {
36724                 this.items[i].setWidth(width);
36725             }
36726         }
36727     },
36728
36729     /**
36730      * Destroys this TabPanel
36731      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
36732      */
36733     destroy : function(removeEl){
36734         Roo.EventManager.removeResizeListener(this.onResize, this);
36735         for(var i = 0, len = this.items.length; i < len; i++){
36736             this.items[i].purgeListeners();
36737         }
36738         if(removeEl === true){
36739             this.el.update("");
36740             this.el.remove();
36741         }
36742     },
36743     
36744     createStrip : function(container)
36745     {
36746         var strip = document.createElement("nav");
36747         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
36748         container.appendChild(strip);
36749         return strip;
36750     },
36751     
36752     createStripList : function(strip)
36753     {
36754         // div wrapper for retard IE
36755         // returns the "tr" element.
36756         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
36757         //'<div class="x-tabs-strip-wrap">'+
36758           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
36759           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
36760         return strip.firstChild; //.firstChild.firstChild.firstChild;
36761     },
36762     createBody : function(container)
36763     {
36764         var body = document.createElement("div");
36765         Roo.id(body, "tab-body");
36766         //Roo.fly(body).addClass("x-tabs-body");
36767         Roo.fly(body).addClass("tab-content");
36768         container.appendChild(body);
36769         return body;
36770     },
36771     createItemBody :function(bodyEl, id){
36772         var body = Roo.getDom(id);
36773         if(!body){
36774             body = document.createElement("div");
36775             body.id = id;
36776         }
36777         //Roo.fly(body).addClass("x-tabs-item-body");
36778         Roo.fly(body).addClass("tab-pane");
36779          bodyEl.insertBefore(body, bodyEl.firstChild);
36780         return body;
36781     },
36782     /** @private */
36783     createStripElements :  function(stripEl, text, closable, tpl)
36784     {
36785         var td = document.createElement("li"); // was td..
36786         
36787         
36788         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
36789         
36790         
36791         stripEl.appendChild(td);
36792         /*if(closable){
36793             td.className = "x-tabs-closable";
36794             if(!this.closeTpl){
36795                 this.closeTpl = new Roo.Template(
36796                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36797                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
36798                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
36799                 );
36800             }
36801             var el = this.closeTpl.overwrite(td, {"text": text});
36802             var close = el.getElementsByTagName("div")[0];
36803             var inner = el.getElementsByTagName("em")[0];
36804             return {"el": el, "close": close, "inner": inner};
36805         } else {
36806         */
36807         // not sure what this is..
36808 //            if(!this.tabTpl){
36809                 //this.tabTpl = new Roo.Template(
36810                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36811                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
36812                 //);
36813 //                this.tabTpl = new Roo.Template(
36814 //                   '<a href="#">' +
36815 //                   '<span unselectable="on"' +
36816 //                            (this.disableTooltips ? '' : ' title="{text}"') +
36817 //                            ' >{text}</span></a>'
36818 //                );
36819 //                
36820 //            }
36821
36822
36823             var template = tpl || this.tabTpl || false;
36824             
36825             if(!template){
36826                 
36827                 template = new Roo.Template(
36828                    '<a href="#">' +
36829                    '<span unselectable="on"' +
36830                             (this.disableTooltips ? '' : ' title="{text}"') +
36831                             ' >{text}</span></a>'
36832                 );
36833             }
36834             
36835             switch (typeof(template)) {
36836                 case 'object' :
36837                     break;
36838                 case 'string' :
36839                     template = new Roo.Template(template);
36840                     break;
36841                 default :
36842                     break;
36843             }
36844             
36845             var el = template.overwrite(td, {"text": text});
36846             
36847             var inner = el.getElementsByTagName("span")[0];
36848             
36849             return {"el": el, "inner": inner};
36850             
36851     }
36852         
36853     
36854 });
36855
36856 /**
36857  * @class Roo.TabPanelItem
36858  * @extends Roo.util.Observable
36859  * Represents an individual item (tab plus body) in a TabPanel.
36860  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
36861  * @param {String} id The id of this TabPanelItem
36862  * @param {String} text The text for the tab of this TabPanelItem
36863  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
36864  */
36865 Roo.bootstrap.panel.TabItem = function(config){
36866     /**
36867      * The {@link Roo.TabPanel} this TabPanelItem belongs to
36868      * @type Roo.TabPanel
36869      */
36870     this.tabPanel = config.panel;
36871     /**
36872      * The id for this TabPanelItem
36873      * @type String
36874      */
36875     this.id = config.id;
36876     /** @private */
36877     this.disabled = false;
36878     /** @private */
36879     this.text = config.text;
36880     /** @private */
36881     this.loaded = false;
36882     this.closable = config.closable;
36883
36884     /**
36885      * The body element for this TabPanelItem.
36886      * @type Roo.Element
36887      */
36888     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
36889     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
36890     this.bodyEl.setStyle("display", "block");
36891     this.bodyEl.setStyle("zoom", "1");
36892     //this.hideAction();
36893
36894     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
36895     /** @private */
36896     this.el = Roo.get(els.el);
36897     this.inner = Roo.get(els.inner, true);
36898     this.textEl = Roo.get(this.el.dom.firstChild, true);
36899     this.pnode = Roo.get(els.el.parentNode, true);
36900     this.el.on("mousedown", this.onTabMouseDown, this);
36901     this.el.on("click", this.onTabClick, this);
36902     /** @private */
36903     if(config.closable){
36904         var c = Roo.get(els.close, true);
36905         c.dom.title = this.closeText;
36906         c.addClassOnOver("close-over");
36907         c.on("click", this.closeClick, this);
36908      }
36909
36910     this.addEvents({
36911          /**
36912          * @event activate
36913          * Fires when this tab becomes the active tab.
36914          * @param {Roo.TabPanel} tabPanel The parent TabPanel
36915          * @param {Roo.TabPanelItem} this
36916          */
36917         "activate": true,
36918         /**
36919          * @event beforeclose
36920          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
36921          * @param {Roo.TabPanelItem} this
36922          * @param {Object} e Set cancel to true on this object to cancel the close.
36923          */
36924         "beforeclose": true,
36925         /**
36926          * @event close
36927          * Fires when this tab is closed.
36928          * @param {Roo.TabPanelItem} this
36929          */
36930          "close": true,
36931         /**
36932          * @event deactivate
36933          * Fires when this tab is no longer the active tab.
36934          * @param {Roo.TabPanel} tabPanel The parent TabPanel
36935          * @param {Roo.TabPanelItem} this
36936          */
36937          "deactivate" : true
36938     });
36939     this.hidden = false;
36940
36941     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
36942 };
36943
36944 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
36945            {
36946     purgeListeners : function(){
36947        Roo.util.Observable.prototype.purgeListeners.call(this);
36948        this.el.removeAllListeners();
36949     },
36950     /**
36951      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
36952      */
36953     show : function(){
36954         this.pnode.addClass("active");
36955         this.showAction();
36956         if(Roo.isOpera){
36957             this.tabPanel.stripWrap.repaint();
36958         }
36959         this.fireEvent("activate", this.tabPanel, this);
36960     },
36961
36962     /**
36963      * Returns true if this tab is the active tab.
36964      * @return {Boolean}
36965      */
36966     isActive : function(){
36967         return this.tabPanel.getActiveTab() == this;
36968     },
36969
36970     /**
36971      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
36972      */
36973     hide : function(){
36974         this.pnode.removeClass("active");
36975         this.hideAction();
36976         this.fireEvent("deactivate", this.tabPanel, this);
36977     },
36978
36979     hideAction : function(){
36980         this.bodyEl.hide();
36981         this.bodyEl.setStyle("position", "absolute");
36982         this.bodyEl.setLeft("-20000px");
36983         this.bodyEl.setTop("-20000px");
36984     },
36985
36986     showAction : function(){
36987         this.bodyEl.setStyle("position", "relative");
36988         this.bodyEl.setTop("");
36989         this.bodyEl.setLeft("");
36990         this.bodyEl.show();
36991     },
36992
36993     /**
36994      * Set the tooltip for the tab.
36995      * @param {String} tooltip The tab's tooltip
36996      */
36997     setTooltip : function(text){
36998         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
36999             this.textEl.dom.qtip = text;
37000             this.textEl.dom.removeAttribute('title');
37001         }else{
37002             this.textEl.dom.title = text;
37003         }
37004     },
37005
37006     onTabClick : function(e){
37007         e.preventDefault();
37008         this.tabPanel.activate(this.id);
37009     },
37010
37011     onTabMouseDown : function(e){
37012         e.preventDefault();
37013         this.tabPanel.activate(this.id);
37014     },
37015 /*
37016     getWidth : function(){
37017         return this.inner.getWidth();
37018     },
37019
37020     setWidth : function(width){
37021         var iwidth = width - this.pnode.getPadding("lr");
37022         this.inner.setWidth(iwidth);
37023         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37024         this.pnode.setWidth(width);
37025     },
37026 */
37027     /**
37028      * Show or hide the tab
37029      * @param {Boolean} hidden True to hide or false to show.
37030      */
37031     setHidden : function(hidden){
37032         this.hidden = hidden;
37033         this.pnode.setStyle("display", hidden ? "none" : "");
37034     },
37035
37036     /**
37037      * Returns true if this tab is "hidden"
37038      * @return {Boolean}
37039      */
37040     isHidden : function(){
37041         return this.hidden;
37042     },
37043
37044     /**
37045      * Returns the text for this tab
37046      * @return {String}
37047      */
37048     getText : function(){
37049         return this.text;
37050     },
37051     /*
37052     autoSize : function(){
37053         //this.el.beginMeasure();
37054         this.textEl.setWidth(1);
37055         /*
37056          *  #2804 [new] Tabs in Roojs
37057          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37058          */
37059         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37060         //this.el.endMeasure();
37061     //},
37062
37063     /**
37064      * Sets the text for the tab (Note: this also sets the tooltip text)
37065      * @param {String} text The tab's text and tooltip
37066      */
37067     setText : function(text){
37068         this.text = text;
37069         this.textEl.update(text);
37070         this.setTooltip(text);
37071         //if(!this.tabPanel.resizeTabs){
37072         //    this.autoSize();
37073         //}
37074     },
37075     /**
37076      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37077      */
37078     activate : function(){
37079         this.tabPanel.activate(this.id);
37080     },
37081
37082     /**
37083      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37084      */
37085     disable : function(){
37086         if(this.tabPanel.active != this){
37087             this.disabled = true;
37088             this.pnode.addClass("disabled");
37089         }
37090     },
37091
37092     /**
37093      * Enables this TabPanelItem if it was previously disabled.
37094      */
37095     enable : function(){
37096         this.disabled = false;
37097         this.pnode.removeClass("disabled");
37098     },
37099
37100     /**
37101      * Sets the content for this TabPanelItem.
37102      * @param {String} content The content
37103      * @param {Boolean} loadScripts true to look for and load scripts
37104      */
37105     setContent : function(content, loadScripts){
37106         this.bodyEl.update(content, loadScripts);
37107     },
37108
37109     /**
37110      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37111      * @return {Roo.UpdateManager} The UpdateManager
37112      */
37113     getUpdateManager : function(){
37114         return this.bodyEl.getUpdateManager();
37115     },
37116
37117     /**
37118      * Set a URL to be used to load the content for this TabPanelItem.
37119      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37120      * @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)
37121      * @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)
37122      * @return {Roo.UpdateManager} The UpdateManager
37123      */
37124     setUrl : function(url, params, loadOnce){
37125         if(this.refreshDelegate){
37126             this.un('activate', this.refreshDelegate);
37127         }
37128         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37129         this.on("activate", this.refreshDelegate);
37130         return this.bodyEl.getUpdateManager();
37131     },
37132
37133     /** @private */
37134     _handleRefresh : function(url, params, loadOnce){
37135         if(!loadOnce || !this.loaded){
37136             var updater = this.bodyEl.getUpdateManager();
37137             updater.update(url, params, this._setLoaded.createDelegate(this));
37138         }
37139     },
37140
37141     /**
37142      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
37143      *   Will fail silently if the setUrl method has not been called.
37144      *   This does not activate the panel, just updates its content.
37145      */
37146     refresh : function(){
37147         if(this.refreshDelegate){
37148            this.loaded = false;
37149            this.refreshDelegate();
37150         }
37151     },
37152
37153     /** @private */
37154     _setLoaded : function(){
37155         this.loaded = true;
37156     },
37157
37158     /** @private */
37159     closeClick : function(e){
37160         var o = {};
37161         e.stopEvent();
37162         this.fireEvent("beforeclose", this, o);
37163         if(o.cancel !== true){
37164             this.tabPanel.removeTab(this.id);
37165         }
37166     },
37167     /**
37168      * The text displayed in the tooltip for the close icon.
37169      * @type String
37170      */
37171     closeText : "Close this tab"
37172 });