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:400,
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                 fitwindow: true,
3350                 modal : true
3351             });
3352             return this;
3353         },
3354
3355         /**
3356          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3357          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3358          * You are responsible for closing the message box when the process is complete.
3359          * @param {String} msg The message box body text
3360          * @param {String} title (optional) The title bar text
3361          * @return {Roo.MessageBox} This message box
3362          */
3363         wait : function(msg, title){
3364             this.show({
3365                 title : title,
3366                 msg : msg,
3367                 buttons: false,
3368                 closable:false,
3369                 progress:true,
3370                 modal:true,
3371                 width:300,
3372                 wait:true
3373             });
3374             waitTimer = Roo.TaskMgr.start({
3375                 run: function(i){
3376                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3377                 },
3378                 interval: 1000
3379             });
3380             return this;
3381         },
3382
3383         /**
3384          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3385          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3386          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3387          * @param {String} title The title bar text
3388          * @param {String} msg The message box body text
3389          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3390          * @param {Object} scope (optional) The scope of the callback function
3391          * @return {Roo.MessageBox} This message box
3392          */
3393         confirm : function(title, msg, fn, scope){
3394             this.show({
3395                 title : title,
3396                 msg : msg,
3397                 buttons: this.YESNO,
3398                 fn: fn,
3399                 scope : scope,
3400                 modal : true
3401             });
3402             return this;
3403         },
3404
3405         /**
3406          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3407          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3408          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3409          * (could also be the top-right close button) and the text that was entered will be passed as the two
3410          * parameters to the callback.
3411          * @param {String} title The title bar text
3412          * @param {String} msg The message box body text
3413          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3414          * @param {Object} scope (optional) The scope of the callback function
3415          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3416          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3417          * @return {Roo.MessageBox} This message box
3418          */
3419         prompt : function(title, msg, fn, scope, multiline){
3420             this.show({
3421                 title : title,
3422                 msg : msg,
3423                 buttons: this.OKCANCEL,
3424                 fn: fn,
3425                 minWidth:250,
3426                 scope : scope,
3427                 prompt:true,
3428                 multiline: multiline,
3429                 modal : true
3430             });
3431             return this;
3432         },
3433
3434         /**
3435          * Button config that displays a single OK button
3436          * @type Object
3437          */
3438         OK : {ok:true},
3439         /**
3440          * Button config that displays Yes and No buttons
3441          * @type Object
3442          */
3443         YESNO : {yes:true, no:true},
3444         /**
3445          * Button config that displays OK and Cancel buttons
3446          * @type Object
3447          */
3448         OKCANCEL : {ok:true, cancel:true},
3449         /**
3450          * Button config that displays Yes, No and Cancel buttons
3451          * @type Object
3452          */
3453         YESNOCANCEL : {yes:true, no:true, cancel:true},
3454
3455         /**
3456          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3457          * @type Number
3458          */
3459         defaultTextHeight : 75,
3460         /**
3461          * The maximum width in pixels of the message box (defaults to 600)
3462          * @type Number
3463          */
3464         maxWidth : 600,
3465         /**
3466          * The minimum width in pixels of the message box (defaults to 100)
3467          * @type Number
3468          */
3469         minWidth : 100,
3470         /**
3471          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3472          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3473          * @type Number
3474          */
3475         minProgressWidth : 250,
3476         /**
3477          * An object containing the default button text strings that can be overriden for localized language support.
3478          * Supported properties are: ok, cancel, yes and no.
3479          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3480          * @type Object
3481          */
3482         buttonText : {
3483             ok : "OK",
3484             cancel : "Cancel",
3485             yes : "Yes",
3486             no : "No"
3487         }
3488     };
3489 }();
3490
3491 /**
3492  * Shorthand for {@link Roo.MessageBox}
3493  */
3494 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3495 Roo.Msg = Roo.Msg || Roo.MessageBox;
3496 /*
3497  * - LGPL
3498  *
3499  * navbar
3500  * 
3501  */
3502
3503 /**
3504  * @class Roo.bootstrap.Navbar
3505  * @extends Roo.bootstrap.Component
3506  * Bootstrap Navbar class
3507
3508  * @constructor
3509  * Create a new Navbar
3510  * @param {Object} config The config object
3511  */
3512
3513
3514 Roo.bootstrap.Navbar = function(config){
3515     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3516     this.addEvents({
3517         // raw events
3518         /**
3519          * @event beforetoggle
3520          * Fire before toggle the menu
3521          * @param {Roo.EventObject} e
3522          */
3523         "beforetoggle" : true
3524     });
3525 };
3526
3527 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3528     
3529     
3530    
3531     // private
3532     navItems : false,
3533     loadMask : false,
3534     
3535     
3536     getAutoCreate : function(){
3537         
3538         
3539         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3540         
3541     },
3542     
3543     initEvents :function ()
3544     {
3545         //Roo.log(this.el.select('.navbar-toggle',true));
3546         this.el.select('.navbar-toggle',true).on('click', function() {
3547             if(this.fireEvent('beforetoggle', this) !== false){
3548                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3549             }
3550             
3551         }, this);
3552         
3553         var mark = {
3554             tag: "div",
3555             cls:"x-dlg-mask"
3556         };
3557         
3558         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3559         
3560         var size = this.el.getSize();
3561         this.maskEl.setSize(size.width, size.height);
3562         this.maskEl.enableDisplayMode("block");
3563         this.maskEl.hide();
3564         
3565         if(this.loadMask){
3566             this.maskEl.show();
3567         }
3568     },
3569     
3570     
3571     getChildContainer : function()
3572     {
3573         if (this.el.select('.collapse').getCount()) {
3574             return this.el.select('.collapse',true).first();
3575         }
3576         
3577         return this.el;
3578     },
3579     
3580     mask : function()
3581     {
3582         this.maskEl.show();
3583     },
3584     
3585     unmask : function()
3586     {
3587         this.maskEl.hide();
3588     } 
3589     
3590     
3591     
3592     
3593 });
3594
3595
3596
3597  
3598
3599  /*
3600  * - LGPL
3601  *
3602  * navbar
3603  * 
3604  */
3605
3606 /**
3607  * @class Roo.bootstrap.NavSimplebar
3608  * @extends Roo.bootstrap.Navbar
3609  * Bootstrap Sidebar class
3610  *
3611  * @cfg {Boolean} inverse is inverted color
3612  * 
3613  * @cfg {String} type (nav | pills | tabs)
3614  * @cfg {Boolean} arrangement stacked | justified
3615  * @cfg {String} align (left | right) alignment
3616  * 
3617  * @cfg {Boolean} main (true|false) main nav bar? default false
3618  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3619  * 
3620  * @cfg {String} tag (header|footer|nav|div) default is nav 
3621
3622  * 
3623  * 
3624  * 
3625  * @constructor
3626  * Create a new Sidebar
3627  * @param {Object} config The config object
3628  */
3629
3630
3631 Roo.bootstrap.NavSimplebar = function(config){
3632     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3633 };
3634
3635 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3636     
3637     inverse: false,
3638     
3639     type: false,
3640     arrangement: '',
3641     align : false,
3642     
3643     
3644     
3645     main : false,
3646     
3647     
3648     tag : false,
3649     
3650     
3651     getAutoCreate : function(){
3652         
3653         
3654         var cfg = {
3655             tag : this.tag || 'div',
3656             cls : 'navbar'
3657         };
3658           
3659         
3660         cfg.cn = [
3661             {
3662                 cls: 'nav',
3663                 tag : 'ul'
3664             }
3665         ];
3666         
3667          
3668         this.type = this.type || 'nav';
3669         if (['tabs','pills'].indexOf(this.type)!==-1) {
3670             cfg.cn[0].cls += ' nav-' + this.type
3671         
3672         
3673         } else {
3674             if (this.type!=='nav') {
3675                 Roo.log('nav type must be nav/tabs/pills')
3676             }
3677             cfg.cn[0].cls += ' navbar-nav'
3678         }
3679         
3680         
3681         
3682         
3683         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3684             cfg.cn[0].cls += ' nav-' + this.arrangement;
3685         }
3686         
3687         
3688         if (this.align === 'right') {
3689             cfg.cn[0].cls += ' navbar-right';
3690         }
3691         
3692         if (this.inverse) {
3693             cfg.cls += ' navbar-inverse';
3694             
3695         }
3696         
3697         
3698         return cfg;
3699     
3700         
3701     }
3702     
3703     
3704     
3705 });
3706
3707
3708
3709  
3710
3711  
3712        /*
3713  * - LGPL
3714  *
3715  * navbar
3716  * 
3717  */
3718
3719 /**
3720  * @class Roo.bootstrap.NavHeaderbar
3721  * @extends Roo.bootstrap.NavSimplebar
3722  * Bootstrap Sidebar class
3723  *
3724  * @cfg {String} brand what is brand
3725  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3726  * @cfg {String} brand_href href of the brand
3727  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3728  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3729  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3730  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3731  * 
3732  * @constructor
3733  * Create a new Sidebar
3734  * @param {Object} config The config object
3735  */
3736
3737
3738 Roo.bootstrap.NavHeaderbar = function(config){
3739     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3740       
3741 };
3742
3743 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3744     
3745     position: '',
3746     brand: '',
3747     brand_href: false,
3748     srButton : true,
3749     autohide : false,
3750     desktopCenter : false,
3751    
3752     
3753     getAutoCreate : function(){
3754         
3755         var   cfg = {
3756             tag: this.nav || 'nav',
3757             cls: 'navbar',
3758             role: 'navigation',
3759             cn: []
3760         };
3761         
3762         var cn = cfg.cn;
3763         if (this.desktopCenter) {
3764             cn.push({cls : 'container', cn : []});
3765             cn = cn[0].cn;
3766         }
3767         
3768         if(this.srButton){
3769             cn.push({
3770                 tag: 'div',
3771                 cls: 'navbar-header',
3772                 cn: [
3773                     {
3774                         tag: 'button',
3775                         type: 'button',
3776                         cls: 'navbar-toggle',
3777                         'data-toggle': 'collapse',
3778                         cn: [
3779                             {
3780                                 tag: 'span',
3781                                 cls: 'sr-only',
3782                                 html: 'Toggle navigation'
3783                             },
3784                             {
3785                                 tag: 'span',
3786                                 cls: 'icon-bar'
3787                             },
3788                             {
3789                                 tag: 'span',
3790                                 cls: 'icon-bar'
3791                             },
3792                             {
3793                                 tag: 'span',
3794                                 cls: 'icon-bar'
3795                             }
3796                         ]
3797                     }
3798                 ]
3799             });
3800         }
3801         
3802         cn.push({
3803             tag: 'div',
3804             cls: 'collapse navbar-collapse',
3805             cn : []
3806         });
3807         
3808         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3809         
3810         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3811             cfg.cls += ' navbar-' + this.position;
3812             
3813             // tag can override this..
3814             
3815             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3816         }
3817         
3818         if (this.brand !== '') {
3819             cn[0].cn.push({
3820                 tag: 'a',
3821                 href: this.brand_href ? this.brand_href : '#',
3822                 cls: 'navbar-brand',
3823                 cn: [
3824                 this.brand
3825                 ]
3826             });
3827         }
3828         
3829         if(this.main){
3830             cfg.cls += ' main-nav';
3831         }
3832         
3833         
3834         return cfg;
3835
3836         
3837     },
3838     getHeaderChildContainer : function()
3839     {
3840         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3841             return this.el.select('.navbar-header',true).first();
3842         }
3843         
3844         return this.getChildContainer();
3845     },
3846     
3847     
3848     initEvents : function()
3849     {
3850         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3851         
3852         if (this.autohide) {
3853             
3854             var prevScroll = 0;
3855             var ft = this.el;
3856             
3857             Roo.get(document).on('scroll',function(e) {
3858                 var ns = Roo.get(document).getScroll().top;
3859                 var os = prevScroll;
3860                 prevScroll = ns;
3861                 
3862                 if(ns > os){
3863                     ft.removeClass('slideDown');
3864                     ft.addClass('slideUp');
3865                     return;
3866                 }
3867                 ft.removeClass('slideUp');
3868                 ft.addClass('slideDown');
3869                  
3870               
3871           },this);
3872         }
3873     }    
3874     
3875 });
3876
3877
3878
3879  
3880
3881  /*
3882  * - LGPL
3883  *
3884  * navbar
3885  * 
3886  */
3887
3888 /**
3889  * @class Roo.bootstrap.NavSidebar
3890  * @extends Roo.bootstrap.Navbar
3891  * Bootstrap Sidebar class
3892  * 
3893  * @constructor
3894  * Create a new Sidebar
3895  * @param {Object} config The config object
3896  */
3897
3898
3899 Roo.bootstrap.NavSidebar = function(config){
3900     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3901 };
3902
3903 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3904     
3905     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3906     
3907     getAutoCreate : function(){
3908         
3909         
3910         return  {
3911             tag: 'div',
3912             cls: 'sidebar sidebar-nav'
3913         };
3914     
3915         
3916     }
3917     
3918     
3919     
3920 });
3921
3922
3923
3924  
3925
3926  /*
3927  * - LGPL
3928  *
3929  * nav group
3930  * 
3931  */
3932
3933 /**
3934  * @class Roo.bootstrap.NavGroup
3935  * @extends Roo.bootstrap.Component
3936  * Bootstrap NavGroup class
3937  * @cfg {String} align (left|right)
3938  * @cfg {Boolean} inverse
3939  * @cfg {String} type (nav|pills|tab) default nav
3940  * @cfg {String} navId - reference Id for navbar.
3941
3942  * 
3943  * @constructor
3944  * Create a new nav group
3945  * @param {Object} config The config object
3946  */
3947
3948 Roo.bootstrap.NavGroup = function(config){
3949     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3950     this.navItems = [];
3951    
3952     Roo.bootstrap.NavGroup.register(this);
3953      this.addEvents({
3954         /**
3955              * @event changed
3956              * Fires when the active item changes
3957              * @param {Roo.bootstrap.NavGroup} this
3958              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3959              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3960          */
3961         'changed': true
3962      });
3963     
3964 };
3965
3966 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3967     
3968     align: '',
3969     inverse: false,
3970     form: false,
3971     type: 'nav',
3972     navId : '',
3973     // private
3974     
3975     navItems : false, 
3976     
3977     getAutoCreate : function()
3978     {
3979         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3980         
3981         cfg = {
3982             tag : 'ul',
3983             cls: 'nav' 
3984         };
3985         
3986         if (['tabs','pills'].indexOf(this.type)!==-1) {
3987             cfg.cls += ' nav-' + this.type
3988         } else {
3989             if (this.type!=='nav') {
3990                 Roo.log('nav type must be nav/tabs/pills')
3991             }
3992             cfg.cls += ' navbar-nav'
3993         }
3994         
3995         if (this.parent().sidebar) {
3996             cfg = {
3997                 tag: 'ul',
3998                 cls: 'dashboard-menu sidebar-menu'
3999             };
4000             
4001             return cfg;
4002         }
4003         
4004         if (this.form === true) {
4005             cfg = {
4006                 tag: 'form',
4007                 cls: 'navbar-form'
4008             };
4009             
4010             if (this.align === 'right') {
4011                 cfg.cls += ' navbar-right';
4012             } else {
4013                 cfg.cls += ' navbar-left';
4014             }
4015         }
4016         
4017         if (this.align === 'right') {
4018             cfg.cls += ' navbar-right';
4019         }
4020         
4021         if (this.inverse) {
4022             cfg.cls += ' navbar-inverse';
4023             
4024         }
4025         
4026         
4027         return cfg;
4028     },
4029     /**
4030     * sets the active Navigation item
4031     * @param {Roo.bootstrap.NavItem} the new current navitem
4032     */
4033     setActiveItem : function(item)
4034     {
4035         var prev = false;
4036         Roo.each(this.navItems, function(v){
4037             if (v == item) {
4038                 return ;
4039             }
4040             if (v.isActive()) {
4041                 v.setActive(false, true);
4042                 prev = v;
4043                 
4044             }
4045             
4046         });
4047
4048         item.setActive(true, true);
4049         this.fireEvent('changed', this, item, prev);
4050         
4051         
4052     },
4053     /**
4054     * gets the active Navigation item
4055     * @return {Roo.bootstrap.NavItem} the current navitem
4056     */
4057     getActive : function()
4058     {
4059         
4060         var prev = false;
4061         Roo.each(this.navItems, function(v){
4062             
4063             if (v.isActive()) {
4064                 prev = v;
4065                 
4066             }
4067             
4068         });
4069         return prev;
4070     },
4071     
4072     indexOfNav : function()
4073     {
4074         
4075         var prev = false;
4076         Roo.each(this.navItems, function(v,i){
4077             
4078             if (v.isActive()) {
4079                 prev = i;
4080                 
4081             }
4082             
4083         });
4084         return prev;
4085     },
4086     /**
4087     * adds a Navigation item
4088     * @param {Roo.bootstrap.NavItem} the navitem to add
4089     */
4090     addItem : function(cfg)
4091     {
4092         var cn = new Roo.bootstrap.NavItem(cfg);
4093         this.register(cn);
4094         cn.parentId = this.id;
4095         cn.onRender(this.el, null);
4096         return cn;
4097     },
4098     /**
4099     * register a Navigation item
4100     * @param {Roo.bootstrap.NavItem} the navitem to add
4101     */
4102     register : function(item)
4103     {
4104         this.navItems.push( item);
4105         item.navId = this.navId;
4106     
4107     },
4108     
4109     /**
4110     * clear all the Navigation item
4111     */
4112    
4113     clearAll : function()
4114     {
4115         this.navItems = [];
4116         this.el.dom.innerHTML = '';
4117     },
4118     
4119     getNavItem: function(tabId)
4120     {
4121         var ret = false;
4122         Roo.each(this.navItems, function(e) {
4123             if (e.tabId == tabId) {
4124                ret =  e;
4125                return false;
4126             }
4127             return true;
4128             
4129         });
4130         return ret;
4131     },
4132     
4133     setActiveNext : function()
4134     {
4135         var i = this.indexOfNav(this.getActive());
4136         if (i > this.navItems.length) {
4137             return;
4138         }
4139         this.setActiveItem(this.navItems[i+1]);
4140     },
4141     setActivePrev : function()
4142     {
4143         var i = this.indexOfNav(this.getActive());
4144         if (i  < 1) {
4145             return;
4146         }
4147         this.setActiveItem(this.navItems[i-1]);
4148     },
4149     clearWasActive : function(except) {
4150         Roo.each(this.navItems, function(e) {
4151             if (e.tabId != except.tabId && e.was_active) {
4152                e.was_active = false;
4153                return false;
4154             }
4155             return true;
4156             
4157         });
4158     },
4159     getWasActive : function ()
4160     {
4161         var r = false;
4162         Roo.each(this.navItems, function(e) {
4163             if (e.was_active) {
4164                r = e;
4165                return false;
4166             }
4167             return true;
4168             
4169         });
4170         return r;
4171     }
4172     
4173     
4174 });
4175
4176  
4177 Roo.apply(Roo.bootstrap.NavGroup, {
4178     
4179     groups: {},
4180      /**
4181     * register a Navigation Group
4182     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4183     */
4184     register : function(navgrp)
4185     {
4186         this.groups[navgrp.navId] = navgrp;
4187         
4188     },
4189     /**
4190     * fetch a Navigation Group based on the navigation ID
4191     * @param {string} the navgroup to add
4192     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4193     */
4194     get: function(navId) {
4195         if (typeof(this.groups[navId]) == 'undefined') {
4196             return false;
4197             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4198         }
4199         return this.groups[navId] ;
4200     }
4201     
4202     
4203     
4204 });
4205
4206  /*
4207  * - LGPL
4208  *
4209  * row
4210  * 
4211  */
4212
4213 /**
4214  * @class Roo.bootstrap.NavItem
4215  * @extends Roo.bootstrap.Component
4216  * Bootstrap Navbar.NavItem class
4217  * @cfg {String} href  link to
4218  * @cfg {String} html content of button
4219  * @cfg {String} badge text inside badge
4220  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4221  * @cfg {String} glyphicon name of glyphicon
4222  * @cfg {String} icon name of font awesome icon
4223  * @cfg {Boolean} active Is item active
4224  * @cfg {Boolean} disabled Is item disabled
4225  
4226  * @cfg {Boolean} preventDefault (true | false) default false
4227  * @cfg {String} tabId the tab that this item activates.
4228  * @cfg {String} tagtype (a|span) render as a href or span?
4229  * @cfg {Boolean} animateRef (true|false) link to element default false  
4230   
4231  * @constructor
4232  * Create a new Navbar Item
4233  * @param {Object} config The config object
4234  */
4235 Roo.bootstrap.NavItem = function(config){
4236     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4237     this.addEvents({
4238         // raw events
4239         /**
4240          * @event click
4241          * The raw click event for the entire grid.
4242          * @param {Roo.EventObject} e
4243          */
4244         "click" : true,
4245          /**
4246             * @event changed
4247             * Fires when the active item active state changes
4248             * @param {Roo.bootstrap.NavItem} this
4249             * @param {boolean} state the new state
4250              
4251          */
4252         'changed': true,
4253         /**
4254             * @event scrollto
4255             * Fires when scroll to element
4256             * @param {Roo.bootstrap.NavItem} this
4257             * @param {Object} options
4258             * @param {Roo.EventObject} e
4259              
4260          */
4261         'scrollto': true
4262     });
4263    
4264 };
4265
4266 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4267     
4268     href: false,
4269     html: '',
4270     badge: '',
4271     icon: false,
4272     glyphicon: false,
4273     active: false,
4274     preventDefault : false,
4275     tabId : false,
4276     tagtype : 'a',
4277     disabled : false,
4278     animateRef : false,
4279     was_active : false,
4280     
4281     getAutoCreate : function(){
4282          
4283         var cfg = {
4284             tag: 'li',
4285             cls: 'nav-item'
4286             
4287         };
4288         
4289         if (this.active) {
4290             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4291         }
4292         if (this.disabled) {
4293             cfg.cls += ' disabled';
4294         }
4295         
4296         if (this.href || this.html || this.glyphicon || this.icon) {
4297             cfg.cn = [
4298                 {
4299                     tag: this.tagtype,
4300                     href : this.href || "#",
4301                     html: this.html || ''
4302                 }
4303             ];
4304             
4305             if (this.icon) {
4306                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4307             }
4308
4309             if(this.glyphicon) {
4310                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4311             }
4312             
4313             if (this.menu) {
4314                 
4315                 cfg.cn[0].html += " <span class='caret'></span>";
4316              
4317             }
4318             
4319             if (this.badge !== '') {
4320                  
4321                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4322             }
4323         }
4324         
4325         
4326         
4327         return cfg;
4328     },
4329     initEvents: function() 
4330     {
4331         if (typeof (this.menu) != 'undefined') {
4332             this.menu.parentType = this.xtype;
4333             this.menu.triggerEl = this.el;
4334             this.menu = this.addxtype(Roo.apply({}, this.menu));
4335         }
4336         
4337         this.el.select('a',true).on('click', this.onClick, this);
4338         
4339         if(this.tagtype == 'span'){
4340             this.el.select('span',true).on('click', this.onClick, this);
4341         }
4342        
4343         // at this point parent should be available..
4344         this.parent().register(this);
4345     },
4346     
4347     onClick : function(e)
4348     {
4349         if (e.getTarget('.dropdown-menu-item')) {
4350             // did you click on a menu itemm.... - then don't trigger onclick..
4351             return;
4352         }
4353         
4354         if(
4355                 this.preventDefault || 
4356                 this.href == '#' 
4357         ){
4358             Roo.log("NavItem - prevent Default?");
4359             e.preventDefault();
4360         }
4361         
4362         if (this.disabled) {
4363             return;
4364         }
4365         
4366         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4367         if (tg && tg.transition) {
4368             Roo.log("waiting for the transitionend");
4369             return;
4370         }
4371         
4372         
4373         
4374         //Roo.log("fire event clicked");
4375         if(this.fireEvent('click', this, e) === false){
4376             return;
4377         };
4378         
4379         if(this.tagtype == 'span'){
4380             return;
4381         }
4382         
4383         //Roo.log(this.href);
4384         var ael = this.el.select('a',true).first();
4385         //Roo.log(ael);
4386         
4387         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4388             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4389             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4390                 return; // ignore... - it's a 'hash' to another page.
4391             }
4392             Roo.log("NavItem - prevent Default?");
4393             e.preventDefault();
4394             this.scrollToElement(e);
4395         }
4396         
4397         
4398         var p =  this.parent();
4399    
4400         if (['tabs','pills'].indexOf(p.type)!==-1) {
4401             if (typeof(p.setActiveItem) !== 'undefined') {
4402                 p.setActiveItem(this);
4403             }
4404         }
4405         
4406         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4407         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4408             // remove the collapsed menu expand...
4409             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4410         }
4411     },
4412     
4413     isActive: function () {
4414         return this.active
4415     },
4416     setActive : function(state, fire, is_was_active)
4417     {
4418         if (this.active && !state && this.navId) {
4419             this.was_active = true;
4420             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4421             if (nv) {
4422                 nv.clearWasActive(this);
4423             }
4424             
4425         }
4426         this.active = state;
4427         
4428         if (!state ) {
4429             this.el.removeClass('active');
4430         } else if (!this.el.hasClass('active')) {
4431             this.el.addClass('active');
4432         }
4433         if (fire) {
4434             this.fireEvent('changed', this, state);
4435         }
4436         
4437         // show a panel if it's registered and related..
4438         
4439         if (!this.navId || !this.tabId || !state || is_was_active) {
4440             return;
4441         }
4442         
4443         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4444         if (!tg) {
4445             return;
4446         }
4447         var pan = tg.getPanelByName(this.tabId);
4448         if (!pan) {
4449             return;
4450         }
4451         // if we can not flip to new panel - go back to old nav highlight..
4452         if (false == tg.showPanel(pan)) {
4453             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4454             if (nv) {
4455                 var onav = nv.getWasActive();
4456                 if (onav) {
4457                     onav.setActive(true, false, true);
4458                 }
4459             }
4460             
4461         }
4462         
4463         
4464         
4465     },
4466      // this should not be here...
4467     setDisabled : function(state)
4468     {
4469         this.disabled = state;
4470         if (!state ) {
4471             this.el.removeClass('disabled');
4472         } else if (!this.el.hasClass('disabled')) {
4473             this.el.addClass('disabled');
4474         }
4475         
4476     },
4477     
4478     /**
4479      * Fetch the element to display the tooltip on.
4480      * @return {Roo.Element} defaults to this.el
4481      */
4482     tooltipEl : function()
4483     {
4484         return this.el.select('' + this.tagtype + '', true).first();
4485     },
4486     
4487     scrollToElement : function(e)
4488     {
4489         var c = document.body;
4490         
4491         /*
4492          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4493          */
4494         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4495             c = document.documentElement;
4496         }
4497         
4498         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4499         
4500         if(!target){
4501             return;
4502         }
4503
4504         var o = target.calcOffsetsTo(c);
4505         
4506         var options = {
4507             target : target,
4508             value : o[1]
4509         };
4510         
4511         this.fireEvent('scrollto', this, options, e);
4512         
4513         Roo.get(c).scrollTo('top', options.value, true);
4514         
4515         return;
4516     }
4517 });
4518  
4519
4520  /*
4521  * - LGPL
4522  *
4523  * sidebar item
4524  *
4525  *  li
4526  *    <span> icon </span>
4527  *    <span> text </span>
4528  *    <span>badge </span>
4529  */
4530
4531 /**
4532  * @class Roo.bootstrap.NavSidebarItem
4533  * @extends Roo.bootstrap.NavItem
4534  * Bootstrap Navbar.NavSidebarItem class
4535  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4536  * {bool} open is the menu open
4537  * @constructor
4538  * Create a new Navbar Button
4539  * @param {Object} config The config object
4540  */
4541 Roo.bootstrap.NavSidebarItem = function(config){
4542     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4543     this.addEvents({
4544         // raw events
4545         /**
4546          * @event click
4547          * The raw click event for the entire grid.
4548          * @param {Roo.EventObject} e
4549          */
4550         "click" : true,
4551          /**
4552             * @event changed
4553             * Fires when the active item active state changes
4554             * @param {Roo.bootstrap.NavSidebarItem} this
4555             * @param {boolean} state the new state
4556              
4557          */
4558         'changed': true
4559     });
4560    
4561 };
4562
4563 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4564     
4565     badgeWeight : 'default',
4566     
4567     open: false,
4568     
4569     getAutoCreate : function(){
4570         
4571         
4572         var a = {
4573                 tag: 'a',
4574                 href : this.href || '#',
4575                 cls: '',
4576                 html : '',
4577                 cn : []
4578         };
4579         var cfg = {
4580             tag: 'li',
4581             cls: '',
4582             cn: [ a ]
4583         };
4584         var span = {
4585             tag: 'span',
4586             html : this.html || ''
4587         };
4588         
4589         
4590         if (this.active) {
4591             cfg.cls += ' active';
4592         }
4593         
4594         if (this.disabled) {
4595             cfg.cls += ' disabled';
4596         }
4597         if (this.open) {
4598             cfg.cls += ' open x-open';
4599         }
4600         // left icon..
4601         if (this.glyphicon || this.icon) {
4602             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4603             a.cn.push({ tag : 'i', cls : c }) ;
4604         }
4605         // html..
4606         a.cn.push(span);
4607         // then badge..
4608         if (this.badge !== '') {
4609             
4610             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4611         }
4612         // fi
4613         if (this.menu) {
4614             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4615             a.cls += 'dropdown-toggle treeview' ;
4616         }
4617         
4618         return cfg;
4619          
4620            
4621     },
4622     
4623     initEvents : function()
4624     { 
4625         if (typeof (this.menu) != 'undefined') {
4626             this.menu.parentType = this.xtype;
4627             this.menu.triggerEl = this.el;
4628             this.menu = this.addxtype(Roo.apply({}, this.menu));
4629         }
4630         
4631         this.el.on('click', this.onClick, this);
4632        
4633     
4634         if(this.badge !== ''){
4635  
4636             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4637         }
4638         
4639     },
4640     
4641     onClick : function(e)
4642     {
4643         if(this.disabled){
4644             e.preventDefault();
4645             return;
4646         }
4647         
4648         if(this.preventDefault){
4649             e.preventDefault();
4650         }
4651         
4652         this.fireEvent('click', this);
4653     },
4654     
4655     disable : function()
4656     {
4657         this.setDisabled(true);
4658     },
4659     
4660     enable : function()
4661     {
4662         this.setDisabled(false);
4663     },
4664     
4665     setDisabled : function(state)
4666     {
4667         if(this.disabled == state){
4668             return;
4669         }
4670         
4671         this.disabled = state;
4672         
4673         if (state) {
4674             this.el.addClass('disabled');
4675             return;
4676         }
4677         
4678         this.el.removeClass('disabled');
4679         
4680         return;
4681     },
4682     
4683     setActive : function(state)
4684     {
4685         if(this.active == state){
4686             return;
4687         }
4688         
4689         this.active = state;
4690         
4691         if (state) {
4692             this.el.addClass('active');
4693             return;
4694         }
4695         
4696         this.el.removeClass('active');
4697         
4698         return;
4699     },
4700     
4701     isActive: function () 
4702     {
4703         return this.active;
4704     },
4705     
4706     setBadge : function(str)
4707     {
4708         if(!this.badgeEl){
4709             return;
4710         }
4711         
4712         this.badgeEl.dom.innerHTML = str;
4713     }
4714     
4715    
4716      
4717  
4718 });
4719  
4720
4721  /*
4722  * - LGPL
4723  *
4724  * row
4725  * 
4726  */
4727
4728 /**
4729  * @class Roo.bootstrap.Row
4730  * @extends Roo.bootstrap.Component
4731  * Bootstrap Row class (contains columns...)
4732  * 
4733  * @constructor
4734  * Create a new Row
4735  * @param {Object} config The config object
4736  */
4737
4738 Roo.bootstrap.Row = function(config){
4739     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4740 };
4741
4742 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4743     
4744     getAutoCreate : function(){
4745        return {
4746             cls: 'row clearfix'
4747        };
4748     }
4749     
4750     
4751 });
4752
4753  
4754
4755  /*
4756  * - LGPL
4757  *
4758  * element
4759  * 
4760  */
4761
4762 /**
4763  * @class Roo.bootstrap.Element
4764  * @extends Roo.bootstrap.Component
4765  * Bootstrap Element class
4766  * @cfg {String} html contents of the element
4767  * @cfg {String} tag tag of the element
4768  * @cfg {String} cls class of the element
4769  * @cfg {Boolean} preventDefault (true|false) default false
4770  * @cfg {Boolean} clickable (true|false) default false
4771  * 
4772  * @constructor
4773  * Create a new Element
4774  * @param {Object} config The config object
4775  */
4776
4777 Roo.bootstrap.Element = function(config){
4778     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4779     
4780     this.addEvents({
4781         // raw events
4782         /**
4783          * @event click
4784          * When a element is chick
4785          * @param {Roo.bootstrap.Element} this
4786          * @param {Roo.EventObject} e
4787          */
4788         "click" : true
4789     });
4790 };
4791
4792 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4793     
4794     tag: 'div',
4795     cls: '',
4796     html: '',
4797     preventDefault: false, 
4798     clickable: false,
4799     
4800     getAutoCreate : function(){
4801         
4802         var cfg = {
4803             tag: this.tag,
4804             cls: this.cls,
4805             html: this.html
4806         };
4807         
4808         return cfg;
4809     },
4810     
4811     initEvents: function() 
4812     {
4813         Roo.bootstrap.Element.superclass.initEvents.call(this);
4814         
4815         if(this.clickable){
4816             this.el.on('click', this.onClick, this);
4817         }
4818         
4819     },
4820     
4821     onClick : function(e)
4822     {
4823         if(this.preventDefault){
4824             e.preventDefault();
4825         }
4826         
4827         this.fireEvent('click', this, e);
4828     },
4829     
4830     getValue : function()
4831     {
4832         return this.el.dom.innerHTML;
4833     },
4834     
4835     setValue : function(value)
4836     {
4837         this.el.dom.innerHTML = value;
4838     }
4839    
4840 });
4841
4842  
4843
4844  /*
4845  * - LGPL
4846  *
4847  * pagination
4848  * 
4849  */
4850
4851 /**
4852  * @class Roo.bootstrap.Pagination
4853  * @extends Roo.bootstrap.Component
4854  * Bootstrap Pagination class
4855  * @cfg {String} size xs | sm | md | lg
4856  * @cfg {Boolean} inverse false | true
4857  * 
4858  * @constructor
4859  * Create a new Pagination
4860  * @param {Object} config The config object
4861  */
4862
4863 Roo.bootstrap.Pagination = function(config){
4864     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4865 };
4866
4867 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4868     
4869     cls: false,
4870     size: false,
4871     inverse: false,
4872     
4873     getAutoCreate : function(){
4874         var cfg = {
4875             tag: 'ul',
4876                 cls: 'pagination'
4877         };
4878         if (this.inverse) {
4879             cfg.cls += ' inverse';
4880         }
4881         if (this.html) {
4882             cfg.html=this.html;
4883         }
4884         if (this.cls) {
4885             cfg.cls += " " + this.cls;
4886         }
4887         return cfg;
4888     }
4889    
4890 });
4891
4892  
4893
4894  /*
4895  * - LGPL
4896  *
4897  * Pagination item
4898  * 
4899  */
4900
4901
4902 /**
4903  * @class Roo.bootstrap.PaginationItem
4904  * @extends Roo.bootstrap.Component
4905  * Bootstrap PaginationItem class
4906  * @cfg {String} html text
4907  * @cfg {String} href the link
4908  * @cfg {Boolean} preventDefault (true | false) default true
4909  * @cfg {Boolean} active (true | false) default false
4910  * @cfg {Boolean} disabled default false
4911  * 
4912  * 
4913  * @constructor
4914  * Create a new PaginationItem
4915  * @param {Object} config The config object
4916  */
4917
4918
4919 Roo.bootstrap.PaginationItem = function(config){
4920     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4921     this.addEvents({
4922         // raw events
4923         /**
4924          * @event click
4925          * The raw click event for the entire grid.
4926          * @param {Roo.EventObject} e
4927          */
4928         "click" : true
4929     });
4930 };
4931
4932 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4933     
4934     href : false,
4935     html : false,
4936     preventDefault: true,
4937     active : false,
4938     cls : false,
4939     disabled: false,
4940     
4941     getAutoCreate : function(){
4942         var cfg= {
4943             tag: 'li',
4944             cn: [
4945                 {
4946                     tag : 'a',
4947                     href : this.href ? this.href : '#',
4948                     html : this.html ? this.html : ''
4949                 }
4950             ]
4951         };
4952         
4953         if(this.cls){
4954             cfg.cls = this.cls;
4955         }
4956         
4957         if(this.disabled){
4958             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4959         }
4960         
4961         if(this.active){
4962             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4963         }
4964         
4965         return cfg;
4966     },
4967     
4968     initEvents: function() {
4969         
4970         this.el.on('click', this.onClick, this);
4971         
4972     },
4973     onClick : function(e)
4974     {
4975         Roo.log('PaginationItem on click ');
4976         if(this.preventDefault){
4977             e.preventDefault();
4978         }
4979         
4980         if(this.disabled){
4981             return;
4982         }
4983         
4984         this.fireEvent('click', this, e);
4985     }
4986    
4987 });
4988
4989  
4990
4991  /*
4992  * - LGPL
4993  *
4994  * slider
4995  * 
4996  */
4997
4998
4999 /**
5000  * @class Roo.bootstrap.Slider
5001  * @extends Roo.bootstrap.Component
5002  * Bootstrap Slider class
5003  *    
5004  * @constructor
5005  * Create a new Slider
5006  * @param {Object} config The config object
5007  */
5008
5009 Roo.bootstrap.Slider = function(config){
5010     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5011 };
5012
5013 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5014     
5015     getAutoCreate : function(){
5016         
5017         var cfg = {
5018             tag: 'div',
5019             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5020             cn: [
5021                 {
5022                     tag: 'a',
5023                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5024                 }
5025             ]
5026         };
5027         
5028         return cfg;
5029     }
5030    
5031 });
5032
5033  /*
5034  * Based on:
5035  * Ext JS Library 1.1.1
5036  * Copyright(c) 2006-2007, Ext JS, LLC.
5037  *
5038  * Originally Released Under LGPL - original licence link has changed is not relivant.
5039  *
5040  * Fork - LGPL
5041  * <script type="text/javascript">
5042  */
5043  
5044
5045 /**
5046  * @class Roo.grid.ColumnModel
5047  * @extends Roo.util.Observable
5048  * This is the default implementation of a ColumnModel used by the Grid. It defines
5049  * the columns in the grid.
5050  * <br>Usage:<br>
5051  <pre><code>
5052  var colModel = new Roo.grid.ColumnModel([
5053         {header: "Ticker", width: 60, sortable: true, locked: true},
5054         {header: "Company Name", width: 150, sortable: true},
5055         {header: "Market Cap.", width: 100, sortable: true},
5056         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5057         {header: "Employees", width: 100, sortable: true, resizable: false}
5058  ]);
5059  </code></pre>
5060  * <p>
5061  
5062  * The config options listed for this class are options which may appear in each
5063  * individual column definition.
5064  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5065  * @constructor
5066  * @param {Object} config An Array of column config objects. See this class's
5067  * config objects for details.
5068 */
5069 Roo.grid.ColumnModel = function(config){
5070         /**
5071      * The config passed into the constructor
5072      */
5073     this.config = config;
5074     this.lookup = {};
5075
5076     // if no id, create one
5077     // if the column does not have a dataIndex mapping,
5078     // map it to the order it is in the config
5079     for(var i = 0, len = config.length; i < len; i++){
5080         var c = config[i];
5081         if(typeof c.dataIndex == "undefined"){
5082             c.dataIndex = i;
5083         }
5084         if(typeof c.renderer == "string"){
5085             c.renderer = Roo.util.Format[c.renderer];
5086         }
5087         if(typeof c.id == "undefined"){
5088             c.id = Roo.id();
5089         }
5090         if(c.editor && c.editor.xtype){
5091             c.editor  = Roo.factory(c.editor, Roo.grid);
5092         }
5093         if(c.editor && c.editor.isFormField){
5094             c.editor = new Roo.grid.GridEditor(c.editor);
5095         }
5096         this.lookup[c.id] = c;
5097     }
5098
5099     /**
5100      * The width of columns which have no width specified (defaults to 100)
5101      * @type Number
5102      */
5103     this.defaultWidth = 100;
5104
5105     /**
5106      * Default sortable of columns which have no sortable specified (defaults to false)
5107      * @type Boolean
5108      */
5109     this.defaultSortable = false;
5110
5111     this.addEvents({
5112         /**
5113              * @event widthchange
5114              * Fires when the width of a column changes.
5115              * @param {ColumnModel} this
5116              * @param {Number} columnIndex The column index
5117              * @param {Number} newWidth The new width
5118              */
5119             "widthchange": true,
5120         /**
5121              * @event headerchange
5122              * Fires when the text of a header changes.
5123              * @param {ColumnModel} this
5124              * @param {Number} columnIndex The column index
5125              * @param {Number} newText The new header text
5126              */
5127             "headerchange": true,
5128         /**
5129              * @event hiddenchange
5130              * Fires when a column is hidden or "unhidden".
5131              * @param {ColumnModel} this
5132              * @param {Number} columnIndex The column index
5133              * @param {Boolean} hidden true if hidden, false otherwise
5134              */
5135             "hiddenchange": true,
5136             /**
5137          * @event columnmoved
5138          * Fires when a column is moved.
5139          * @param {ColumnModel} this
5140          * @param {Number} oldIndex
5141          * @param {Number} newIndex
5142          */
5143         "columnmoved" : true,
5144         /**
5145          * @event columlockchange
5146          * Fires when a column's locked state is changed
5147          * @param {ColumnModel} this
5148          * @param {Number} colIndex
5149          * @param {Boolean} locked true if locked
5150          */
5151         "columnlockchange" : true
5152     });
5153     Roo.grid.ColumnModel.superclass.constructor.call(this);
5154 };
5155 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5156     /**
5157      * @cfg {String} header The header text to display in the Grid view.
5158      */
5159     /**
5160      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5161      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5162      * specified, the column's index is used as an index into the Record's data Array.
5163      */
5164     /**
5165      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5166      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5167      */
5168     /**
5169      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5170      * Defaults to the value of the {@link #defaultSortable} property.
5171      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5172      */
5173     /**
5174      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5175      */
5176     /**
5177      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5178      */
5179     /**
5180      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5181      */
5182     /**
5183      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5184      */
5185     /**
5186      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5187      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5188      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5189      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5190      */
5191        /**
5192      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5193      */
5194     /**
5195      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5196      */
5197     /**
5198      * @cfg {String} cursor (Optional)
5199      */
5200     /**
5201      * @cfg {String} tooltip (Optional)
5202      */
5203     /**
5204      * @cfg {Number} xs (Optional)
5205      */
5206     /**
5207      * @cfg {Number} sm (Optional)
5208      */
5209     /**
5210      * @cfg {Number} md (Optional)
5211      */
5212     /**
5213      * @cfg {Number} lg (Optional)
5214      */
5215     /**
5216      * Returns the id of the column at the specified index.
5217      * @param {Number} index The column index
5218      * @return {String} the id
5219      */
5220     getColumnId : function(index){
5221         return this.config[index].id;
5222     },
5223
5224     /**
5225      * Returns the column for a specified id.
5226      * @param {String} id The column id
5227      * @return {Object} the column
5228      */
5229     getColumnById : function(id){
5230         return this.lookup[id];
5231     },
5232
5233     
5234     /**
5235      * Returns the column for a specified dataIndex.
5236      * @param {String} dataIndex The column dataIndex
5237      * @return {Object|Boolean} the column or false if not found
5238      */
5239     getColumnByDataIndex: function(dataIndex){
5240         var index = this.findColumnIndex(dataIndex);
5241         return index > -1 ? this.config[index] : false;
5242     },
5243     
5244     /**
5245      * Returns the index for a specified column id.
5246      * @param {String} id The column id
5247      * @return {Number} the index, or -1 if not found
5248      */
5249     getIndexById : function(id){
5250         for(var i = 0, len = this.config.length; i < len; i++){
5251             if(this.config[i].id == id){
5252                 return i;
5253             }
5254         }
5255         return -1;
5256     },
5257     
5258     /**
5259      * Returns the index for a specified column dataIndex.
5260      * @param {String} dataIndex The column dataIndex
5261      * @return {Number} the index, or -1 if not found
5262      */
5263     
5264     findColumnIndex : function(dataIndex){
5265         for(var i = 0, len = this.config.length; i < len; i++){
5266             if(this.config[i].dataIndex == dataIndex){
5267                 return i;
5268             }
5269         }
5270         return -1;
5271     },
5272     
5273     
5274     moveColumn : function(oldIndex, newIndex){
5275         var c = this.config[oldIndex];
5276         this.config.splice(oldIndex, 1);
5277         this.config.splice(newIndex, 0, c);
5278         this.dataMap = null;
5279         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5280     },
5281
5282     isLocked : function(colIndex){
5283         return this.config[colIndex].locked === true;
5284     },
5285
5286     setLocked : function(colIndex, value, suppressEvent){
5287         if(this.isLocked(colIndex) == value){
5288             return;
5289         }
5290         this.config[colIndex].locked = value;
5291         if(!suppressEvent){
5292             this.fireEvent("columnlockchange", this, colIndex, value);
5293         }
5294     },
5295
5296     getTotalLockedWidth : function(){
5297         var totalWidth = 0;
5298         for(var i = 0; i < this.config.length; i++){
5299             if(this.isLocked(i) && !this.isHidden(i)){
5300                 this.totalWidth += this.getColumnWidth(i);
5301             }
5302         }
5303         return totalWidth;
5304     },
5305
5306     getLockedCount : function(){
5307         for(var i = 0, len = this.config.length; i < len; i++){
5308             if(!this.isLocked(i)){
5309                 return i;
5310             }
5311         }
5312         
5313         return this.config.length;
5314     },
5315
5316     /**
5317      * Returns the number of columns.
5318      * @return {Number}
5319      */
5320     getColumnCount : function(visibleOnly){
5321         if(visibleOnly === true){
5322             var c = 0;
5323             for(var i = 0, len = this.config.length; i < len; i++){
5324                 if(!this.isHidden(i)){
5325                     c++;
5326                 }
5327             }
5328             return c;
5329         }
5330         return this.config.length;
5331     },
5332
5333     /**
5334      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5335      * @param {Function} fn
5336      * @param {Object} scope (optional)
5337      * @return {Array} result
5338      */
5339     getColumnsBy : function(fn, scope){
5340         var r = [];
5341         for(var i = 0, len = this.config.length; i < len; i++){
5342             var c = this.config[i];
5343             if(fn.call(scope||this, c, i) === true){
5344                 r[r.length] = c;
5345             }
5346         }
5347         return r;
5348     },
5349
5350     /**
5351      * Returns true if the specified column is sortable.
5352      * @param {Number} col The column index
5353      * @return {Boolean}
5354      */
5355     isSortable : function(col){
5356         if(typeof this.config[col].sortable == "undefined"){
5357             return this.defaultSortable;
5358         }
5359         return this.config[col].sortable;
5360     },
5361
5362     /**
5363      * Returns the rendering (formatting) function defined for the column.
5364      * @param {Number} col The column index.
5365      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5366      */
5367     getRenderer : function(col){
5368         if(!this.config[col].renderer){
5369             return Roo.grid.ColumnModel.defaultRenderer;
5370         }
5371         return this.config[col].renderer;
5372     },
5373
5374     /**
5375      * Sets the rendering (formatting) function for a column.
5376      * @param {Number} col The column index
5377      * @param {Function} fn The function to use to process the cell's raw data
5378      * to return HTML markup for the grid view. The render function is called with
5379      * the following parameters:<ul>
5380      * <li>Data value.</li>
5381      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5382      * <li>css A CSS style string to apply to the table cell.</li>
5383      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5384      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5385      * <li>Row index</li>
5386      * <li>Column index</li>
5387      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5388      */
5389     setRenderer : function(col, fn){
5390         this.config[col].renderer = fn;
5391     },
5392
5393     /**
5394      * Returns the width for the specified column.
5395      * @param {Number} col The column index
5396      * @return {Number}
5397      */
5398     getColumnWidth : function(col){
5399         return this.config[col].width * 1 || this.defaultWidth;
5400     },
5401
5402     /**
5403      * Sets the width for a column.
5404      * @param {Number} col The column index
5405      * @param {Number} width The new width
5406      */
5407     setColumnWidth : function(col, width, suppressEvent){
5408         this.config[col].width = width;
5409         this.totalWidth = null;
5410         if(!suppressEvent){
5411              this.fireEvent("widthchange", this, col, width);
5412         }
5413     },
5414
5415     /**
5416      * Returns the total width of all columns.
5417      * @param {Boolean} includeHidden True to include hidden column widths
5418      * @return {Number}
5419      */
5420     getTotalWidth : function(includeHidden){
5421         if(!this.totalWidth){
5422             this.totalWidth = 0;
5423             for(var i = 0, len = this.config.length; i < len; i++){
5424                 if(includeHidden || !this.isHidden(i)){
5425                     this.totalWidth += this.getColumnWidth(i);
5426                 }
5427             }
5428         }
5429         return this.totalWidth;
5430     },
5431
5432     /**
5433      * Returns the header for the specified column.
5434      * @param {Number} col The column index
5435      * @return {String}
5436      */
5437     getColumnHeader : function(col){
5438         return this.config[col].header;
5439     },
5440
5441     /**
5442      * Sets the header for a column.
5443      * @param {Number} col The column index
5444      * @param {String} header The new header
5445      */
5446     setColumnHeader : function(col, header){
5447         this.config[col].header = header;
5448         this.fireEvent("headerchange", this, col, header);
5449     },
5450
5451     /**
5452      * Returns the tooltip for the specified column.
5453      * @param {Number} col The column index
5454      * @return {String}
5455      */
5456     getColumnTooltip : function(col){
5457             return this.config[col].tooltip;
5458     },
5459     /**
5460      * Sets the tooltip for a column.
5461      * @param {Number} col The column index
5462      * @param {String} tooltip The new tooltip
5463      */
5464     setColumnTooltip : function(col, tooltip){
5465             this.config[col].tooltip = tooltip;
5466     },
5467
5468     /**
5469      * Returns the dataIndex for the specified column.
5470      * @param {Number} col The column index
5471      * @return {Number}
5472      */
5473     getDataIndex : function(col){
5474         return this.config[col].dataIndex;
5475     },
5476
5477     /**
5478      * Sets the dataIndex for a column.
5479      * @param {Number} col The column index
5480      * @param {Number} dataIndex The new dataIndex
5481      */
5482     setDataIndex : function(col, dataIndex){
5483         this.config[col].dataIndex = dataIndex;
5484     },
5485
5486     
5487     
5488     /**
5489      * Returns true if the cell is editable.
5490      * @param {Number} colIndex The column index
5491      * @param {Number} rowIndex The row index - this is nto actually used..?
5492      * @return {Boolean}
5493      */
5494     isCellEditable : function(colIndex, rowIndex){
5495         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5496     },
5497
5498     /**
5499      * Returns the editor defined for the cell/column.
5500      * return false or null to disable editing.
5501      * @param {Number} colIndex The column index
5502      * @param {Number} rowIndex The row index
5503      * @return {Object}
5504      */
5505     getCellEditor : function(colIndex, rowIndex){
5506         return this.config[colIndex].editor;
5507     },
5508
5509     /**
5510      * Sets if a column is editable.
5511      * @param {Number} col The column index
5512      * @param {Boolean} editable True if the column is editable
5513      */
5514     setEditable : function(col, editable){
5515         this.config[col].editable = editable;
5516     },
5517
5518
5519     /**
5520      * Returns true if the column is hidden.
5521      * @param {Number} colIndex The column index
5522      * @return {Boolean}
5523      */
5524     isHidden : function(colIndex){
5525         return this.config[colIndex].hidden;
5526     },
5527
5528
5529     /**
5530      * Returns true if the column width cannot be changed
5531      */
5532     isFixed : function(colIndex){
5533         return this.config[colIndex].fixed;
5534     },
5535
5536     /**
5537      * Returns true if the column can be resized
5538      * @return {Boolean}
5539      */
5540     isResizable : function(colIndex){
5541         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5542     },
5543     /**
5544      * Sets if a column is hidden.
5545      * @param {Number} colIndex The column index
5546      * @param {Boolean} hidden True if the column is hidden
5547      */
5548     setHidden : function(colIndex, hidden){
5549         this.config[colIndex].hidden = hidden;
5550         this.totalWidth = null;
5551         this.fireEvent("hiddenchange", this, colIndex, hidden);
5552     },
5553
5554     /**
5555      * Sets the editor for a column.
5556      * @param {Number} col The column index
5557      * @param {Object} editor The editor object
5558      */
5559     setEditor : function(col, editor){
5560         this.config[col].editor = editor;
5561     }
5562 });
5563
5564 Roo.grid.ColumnModel.defaultRenderer = function(value)
5565 {
5566     if(typeof value == "object") {
5567         return value;
5568     }
5569         if(typeof value == "string" && value.length < 1){
5570             return "&#160;";
5571         }
5572     
5573         return String.format("{0}", value);
5574 };
5575
5576 // Alias for backwards compatibility
5577 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5578 /*
5579  * Based on:
5580  * Ext JS Library 1.1.1
5581  * Copyright(c) 2006-2007, Ext JS, LLC.
5582  *
5583  * Originally Released Under LGPL - original licence link has changed is not relivant.
5584  *
5585  * Fork - LGPL
5586  * <script type="text/javascript">
5587  */
5588  
5589 /**
5590  * @class Roo.LoadMask
5591  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5592  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5593  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5594  * element's UpdateManager load indicator and will be destroyed after the initial load.
5595  * @constructor
5596  * Create a new LoadMask
5597  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5598  * @param {Object} config The config object
5599  */
5600 Roo.LoadMask = function(el, config){
5601     this.el = Roo.get(el);
5602     Roo.apply(this, config);
5603     if(this.store){
5604         this.store.on('beforeload', this.onBeforeLoad, this);
5605         this.store.on('load', this.onLoad, this);
5606         this.store.on('loadexception', this.onLoadException, this);
5607         this.removeMask = false;
5608     }else{
5609         var um = this.el.getUpdateManager();
5610         um.showLoadIndicator = false; // disable the default indicator
5611         um.on('beforeupdate', this.onBeforeLoad, this);
5612         um.on('update', this.onLoad, this);
5613         um.on('failure', this.onLoad, this);
5614         this.removeMask = true;
5615     }
5616 };
5617
5618 Roo.LoadMask.prototype = {
5619     /**
5620      * @cfg {Boolean} removeMask
5621      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5622      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5623      */
5624     /**
5625      * @cfg {String} msg
5626      * The text to display in a centered loading message box (defaults to 'Loading...')
5627      */
5628     msg : 'Loading...',
5629     /**
5630      * @cfg {String} msgCls
5631      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5632      */
5633     msgCls : 'x-mask-loading',
5634
5635     /**
5636      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5637      * @type Boolean
5638      */
5639     disabled: false,
5640
5641     /**
5642      * Disables the mask to prevent it from being displayed
5643      */
5644     disable : function(){
5645        this.disabled = true;
5646     },
5647
5648     /**
5649      * Enables the mask so that it can be displayed
5650      */
5651     enable : function(){
5652         this.disabled = false;
5653     },
5654     
5655     onLoadException : function()
5656     {
5657         Roo.log(arguments);
5658         
5659         if (typeof(arguments[3]) != 'undefined') {
5660             Roo.MessageBox.alert("Error loading",arguments[3]);
5661         } 
5662         /*
5663         try {
5664             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5665                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5666             }   
5667         } catch(e) {
5668             
5669         }
5670         */
5671     
5672         
5673         
5674         this.el.unmask(this.removeMask);
5675     },
5676     // private
5677     onLoad : function()
5678     {
5679         this.el.unmask(this.removeMask);
5680     },
5681
5682     // private
5683     onBeforeLoad : function(){
5684         if(!this.disabled){
5685             this.el.mask(this.msg, this.msgCls);
5686         }
5687     },
5688
5689     // private
5690     destroy : function(){
5691         if(this.store){
5692             this.store.un('beforeload', this.onBeforeLoad, this);
5693             this.store.un('load', this.onLoad, this);
5694             this.store.un('loadexception', this.onLoadException, this);
5695         }else{
5696             var um = this.el.getUpdateManager();
5697             um.un('beforeupdate', this.onBeforeLoad, this);
5698             um.un('update', this.onLoad, this);
5699             um.un('failure', this.onLoad, this);
5700         }
5701     }
5702 };/*
5703  * - LGPL
5704  *
5705  * table
5706  * 
5707  */
5708
5709 /**
5710  * @class Roo.bootstrap.Table
5711  * @extends Roo.bootstrap.Component
5712  * Bootstrap Table class
5713  * @cfg {String} cls table class
5714  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5715  * @cfg {String} bgcolor Specifies the background color for a table
5716  * @cfg {Number} border Specifies whether the table cells should have borders or not
5717  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5718  * @cfg {Number} cellspacing Specifies the space between cells
5719  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5720  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5721  * @cfg {String} sortable Specifies that the table should be sortable
5722  * @cfg {String} summary Specifies a summary of the content of a table
5723  * @cfg {Number} width Specifies the width of a table
5724  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5725  * 
5726  * @cfg {boolean} striped Should the rows be alternative striped
5727  * @cfg {boolean} bordered Add borders to the table
5728  * @cfg {boolean} hover Add hover highlighting
5729  * @cfg {boolean} condensed Format condensed
5730  * @cfg {boolean} responsive Format condensed
5731  * @cfg {Boolean} loadMask (true|false) default false
5732  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5733  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5734  * @cfg {Boolean} rowSelection (true|false) default false
5735  * @cfg {Boolean} cellSelection (true|false) default false
5736  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5737  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5738  
5739  * 
5740  * @constructor
5741  * Create a new Table
5742  * @param {Object} config The config object
5743  */
5744
5745 Roo.bootstrap.Table = function(config){
5746     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5747     
5748   
5749     
5750     // BC...
5751     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5752     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5753     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5754     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5755     
5756     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5757     if (this.sm) {
5758         this.sm.grid = this;
5759         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5760         this.sm = this.selModel;
5761         this.sm.xmodule = this.xmodule || false;
5762     }
5763     
5764     if (this.cm && typeof(this.cm.config) == 'undefined') {
5765         this.colModel = new Roo.grid.ColumnModel(this.cm);
5766         this.cm = this.colModel;
5767         this.cm.xmodule = this.xmodule || false;
5768     }
5769     if (this.store) {
5770         this.store= Roo.factory(this.store, Roo.data);
5771         this.ds = this.store;
5772         this.ds.xmodule = this.xmodule || false;
5773          
5774     }
5775     if (this.footer && this.store) {
5776         this.footer.dataSource = this.ds;
5777         this.footer = Roo.factory(this.footer);
5778     }
5779     
5780     /** @private */
5781     this.addEvents({
5782         /**
5783          * @event cellclick
5784          * Fires when a cell is clicked
5785          * @param {Roo.bootstrap.Table} this
5786          * @param {Roo.Element} el
5787          * @param {Number} rowIndex
5788          * @param {Number} columnIndex
5789          * @param {Roo.EventObject} e
5790          */
5791         "cellclick" : true,
5792         /**
5793          * @event celldblclick
5794          * Fires when a cell is double clicked
5795          * @param {Roo.bootstrap.Table} this
5796          * @param {Roo.Element} el
5797          * @param {Number} rowIndex
5798          * @param {Number} columnIndex
5799          * @param {Roo.EventObject} e
5800          */
5801         "celldblclick" : true,
5802         /**
5803          * @event rowclick
5804          * Fires when a row is clicked
5805          * @param {Roo.bootstrap.Table} this
5806          * @param {Roo.Element} el
5807          * @param {Number} rowIndex
5808          * @param {Roo.EventObject} e
5809          */
5810         "rowclick" : true,
5811         /**
5812          * @event rowdblclick
5813          * Fires when a row is double clicked
5814          * @param {Roo.bootstrap.Table} this
5815          * @param {Roo.Element} el
5816          * @param {Number} rowIndex
5817          * @param {Roo.EventObject} e
5818          */
5819         "rowdblclick" : true,
5820         /**
5821          * @event mouseover
5822          * Fires when a mouseover occur
5823          * @param {Roo.bootstrap.Table} this
5824          * @param {Roo.Element} el
5825          * @param {Number} rowIndex
5826          * @param {Number} columnIndex
5827          * @param {Roo.EventObject} e
5828          */
5829         "mouseover" : true,
5830         /**
5831          * @event mouseout
5832          * Fires when a mouseout occur
5833          * @param {Roo.bootstrap.Table} this
5834          * @param {Roo.Element} el
5835          * @param {Number} rowIndex
5836          * @param {Number} columnIndex
5837          * @param {Roo.EventObject} e
5838          */
5839         "mouseout" : true,
5840         /**
5841          * @event rowclass
5842          * Fires when a row is rendered, so you can change add a style to it.
5843          * @param {Roo.bootstrap.Table} this
5844          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5845          */
5846         'rowclass' : true,
5847           /**
5848          * @event rowsrendered
5849          * Fires when all the  rows have been rendered
5850          * @param {Roo.bootstrap.Table} this
5851          */
5852         'rowsrendered' : true,
5853         /**
5854          * @event contextmenu
5855          * The raw contextmenu event for the entire grid.
5856          * @param {Roo.EventObject} e
5857          */
5858         "contextmenu" : true,
5859         /**
5860          * @event rowcontextmenu
5861          * Fires when a row is right clicked
5862          * @param {Roo.bootstrap.Table} this
5863          * @param {Number} rowIndex
5864          * @param {Roo.EventObject} e
5865          */
5866         "rowcontextmenu" : true,
5867         /**
5868          * @event cellcontextmenu
5869          * Fires when a cell is right clicked
5870          * @param {Roo.bootstrap.Table} this
5871          * @param {Number} rowIndex
5872          * @param {Number} cellIndex
5873          * @param {Roo.EventObject} e
5874          */
5875          "cellcontextmenu" : true,
5876          /**
5877          * @event headercontextmenu
5878          * Fires when a header is right clicked
5879          * @param {Roo.bootstrap.Table} this
5880          * @param {Number} columnIndex
5881          * @param {Roo.EventObject} e
5882          */
5883         "headercontextmenu" : true
5884     });
5885 };
5886
5887 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5888     
5889     cls: false,
5890     align: false,
5891     bgcolor: false,
5892     border: false,
5893     cellpadding: false,
5894     cellspacing: false,
5895     frame: false,
5896     rules: false,
5897     sortable: false,
5898     summary: false,
5899     width: false,
5900     striped : false,
5901     scrollBody : false,
5902     bordered: false,
5903     hover:  false,
5904     condensed : false,
5905     responsive : false,
5906     sm : false,
5907     cm : false,
5908     store : false,
5909     loadMask : false,
5910     footerShow : true,
5911     headerShow : true,
5912   
5913     rowSelection : false,
5914     cellSelection : false,
5915     layout : false,
5916     
5917     // Roo.Element - the tbody
5918     mainBody: false,
5919     // Roo.Element - thead element
5920     mainHead: false,
5921     
5922     container: false, // used by gridpanel...
5923     
5924     getAutoCreate : function()
5925     {
5926         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5927         
5928         cfg = {
5929             tag: 'table',
5930             cls : 'table',
5931             cn : []
5932         };
5933         if (this.scrollBody) {
5934             cfg.cls += ' table-body-fixed';
5935         }    
5936         if (this.striped) {
5937             cfg.cls += ' table-striped';
5938         }
5939         
5940         if (this.hover) {
5941             cfg.cls += ' table-hover';
5942         }
5943         if (this.bordered) {
5944             cfg.cls += ' table-bordered';
5945         }
5946         if (this.condensed) {
5947             cfg.cls += ' table-condensed';
5948         }
5949         if (this.responsive) {
5950             cfg.cls += ' table-responsive';
5951         }
5952         
5953         if (this.cls) {
5954             cfg.cls+=  ' ' +this.cls;
5955         }
5956         
5957         // this lot should be simplifed...
5958         
5959         if (this.align) {
5960             cfg.align=this.align;
5961         }
5962         if (this.bgcolor) {
5963             cfg.bgcolor=this.bgcolor;
5964         }
5965         if (this.border) {
5966             cfg.border=this.border;
5967         }
5968         if (this.cellpadding) {
5969             cfg.cellpadding=this.cellpadding;
5970         }
5971         if (this.cellspacing) {
5972             cfg.cellspacing=this.cellspacing;
5973         }
5974         if (this.frame) {
5975             cfg.frame=this.frame;
5976         }
5977         if (this.rules) {
5978             cfg.rules=this.rules;
5979         }
5980         if (this.sortable) {
5981             cfg.sortable=this.sortable;
5982         }
5983         if (this.summary) {
5984             cfg.summary=this.summary;
5985         }
5986         if (this.width) {
5987             cfg.width=this.width;
5988         }
5989         if (this.layout) {
5990             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5991         }
5992         
5993         if(this.store || this.cm){
5994             if(this.headerShow){
5995                 cfg.cn.push(this.renderHeader());
5996             }
5997             
5998             cfg.cn.push(this.renderBody());
5999             
6000             if(this.footerShow){
6001                 cfg.cn.push(this.renderFooter());
6002             }
6003             // where does this come from?
6004             //cfg.cls+=  ' TableGrid';
6005         }
6006         
6007         return { cn : [ cfg ] };
6008     },
6009     
6010     initEvents : function()
6011     {   
6012         if(!this.store || !this.cm){
6013             return;
6014         }
6015         if (this.selModel) {
6016             this.selModel.initEvents();
6017         }
6018         
6019         
6020         //Roo.log('initEvents with ds!!!!');
6021         
6022         this.mainBody = this.el.select('tbody', true).first();
6023         this.mainHead = this.el.select('thead', true).first();
6024         
6025         
6026         
6027         
6028         var _this = this;
6029         
6030         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6031             e.on('click', _this.sort, _this);
6032         });
6033         
6034         this.mainBody.on("click", this.onClick, this);
6035         this.mainBody.on("dblclick", this.onDblClick, this);
6036         
6037         // why is this done????? = it breaks dialogs??
6038         //this.parent().el.setStyle('position', 'relative');
6039         
6040         
6041         if (this.footer) {
6042             this.footer.parentId = this.id;
6043             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
6044         } 
6045         
6046         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6047         
6048         this.store.on('load', this.onLoad, this);
6049         this.store.on('beforeload', this.onBeforeLoad, this);
6050         this.store.on('update', this.onUpdate, this);
6051         this.store.on('add', this.onAdd, this);
6052         this.store.on("clear", this.clear, this);
6053         
6054         this.el.on("contextmenu", this.onContextMenu, this);
6055         
6056         this.mainBody.on('scroll', this.onBodyScroll, this);
6057         
6058         
6059     },
6060     
6061     onContextMenu : function(e, t)
6062     {
6063         this.processEvent("contextmenu", e);
6064     },
6065     
6066     processEvent : function(name, e)
6067     {
6068         if (name != 'touchstart' ) {
6069             this.fireEvent(name, e);    
6070         }
6071         
6072         var t = e.getTarget();
6073         
6074         var cell = Roo.get(t);
6075         
6076         if(!cell){
6077             return;
6078         }
6079         
6080         if(cell.findParent('tfoot', false, true)){
6081             return;
6082         }
6083         
6084         if(cell.findParent('thead', false, true)){
6085             
6086             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6087                 cell = Roo.get(t).findParent('th', false, true);
6088                 if (!cell) {
6089                     Roo.log("failed to find th in thead?");
6090                     Roo.log(e.getTarget());
6091                     return;
6092                 }
6093             }
6094             
6095             var cellIndex = cell.dom.cellIndex;
6096             
6097             var ename = name == 'touchstart' ? 'click' : name;
6098             this.fireEvent("header" + ename, this, cellIndex, e);
6099             
6100             return;
6101         }
6102         
6103         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6104             cell = Roo.get(t).findParent('td', false, true);
6105             if (!cell) {
6106                 Roo.log("failed to find th in tbody?");
6107                 Roo.log(e.getTarget());
6108                 return;
6109             }
6110         }
6111         
6112         var row = cell.findParent('tr', false, true);
6113         var cellIndex = cell.dom.cellIndex;
6114         var rowIndex = row.dom.rowIndex - 1;
6115         
6116         if(row !== false){
6117             
6118             this.fireEvent("row" + name, this, rowIndex, e);
6119             
6120             if(cell !== false){
6121             
6122                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6123             }
6124         }
6125         
6126     },
6127     
6128     onMouseover : function(e, el)
6129     {
6130         var cell = Roo.get(el);
6131         
6132         if(!cell){
6133             return;
6134         }
6135         
6136         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6137             cell = cell.findParent('td', false, true);
6138         }
6139         
6140         var row = cell.findParent('tr', false, true);
6141         var cellIndex = cell.dom.cellIndex;
6142         var rowIndex = row.dom.rowIndex - 1; // start from 0
6143         
6144         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6145         
6146     },
6147     
6148     onMouseout : function(e, el)
6149     {
6150         var cell = Roo.get(el);
6151         
6152         if(!cell){
6153             return;
6154         }
6155         
6156         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6157             cell = cell.findParent('td', false, true);
6158         }
6159         
6160         var row = cell.findParent('tr', false, true);
6161         var cellIndex = cell.dom.cellIndex;
6162         var rowIndex = row.dom.rowIndex - 1; // start from 0
6163         
6164         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6165         
6166     },
6167     
6168     onClick : function(e, el)
6169     {
6170         var cell = Roo.get(el);
6171         
6172         if(!cell || (!this.cellSelection && !this.rowSelection)){
6173             return;
6174         }
6175         
6176         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6177             cell = cell.findParent('td', false, true);
6178         }
6179         
6180         if(!cell || typeof(cell) == 'undefined'){
6181             return;
6182         }
6183         
6184         var row = cell.findParent('tr', false, true);
6185         
6186         if(!row || typeof(row) == 'undefined'){
6187             return;
6188         }
6189         
6190         var cellIndex = cell.dom.cellIndex;
6191         var rowIndex = this.getRowIndex(row);
6192         
6193         // why??? - should these not be based on SelectionModel?
6194         if(this.cellSelection){
6195             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6196         }
6197         
6198         if(this.rowSelection){
6199             this.fireEvent('rowclick', this, row, rowIndex, e);
6200         }
6201         
6202         
6203     },
6204         
6205     onDblClick : function(e,el)
6206     {
6207         var cell = Roo.get(el);
6208         
6209         if(!cell || (!this.cellSelection && !this.rowSelection)){
6210             return;
6211         }
6212         
6213         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6214             cell = cell.findParent('td', false, true);
6215         }
6216         
6217         if(!cell || typeof(cell) == 'undefined'){
6218             return;
6219         }
6220         
6221         var row = cell.findParent('tr', false, true);
6222         
6223         if(!row || typeof(row) == 'undefined'){
6224             return;
6225         }
6226         
6227         var cellIndex = cell.dom.cellIndex;
6228         var rowIndex = this.getRowIndex(row);
6229         
6230         if(this.cellSelection){
6231             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6232         }
6233         
6234         if(this.rowSelection){
6235             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6236         }
6237     },
6238     
6239     sort : function(e,el)
6240     {
6241         var col = Roo.get(el);
6242         
6243         if(!col.hasClass('sortable')){
6244             return;
6245         }
6246         
6247         var sort = col.attr('sort');
6248         var dir = 'ASC';
6249         
6250         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6251             dir = 'DESC';
6252         }
6253         
6254         this.store.sortInfo = {field : sort, direction : dir};
6255         
6256         if (this.footer) {
6257             Roo.log("calling footer first");
6258             this.footer.onClick('first');
6259         } else {
6260         
6261             this.store.load({ params : { start : 0 } });
6262         }
6263     },
6264     
6265     renderHeader : function()
6266     {
6267         var header = {
6268             tag: 'thead',
6269             cn : []
6270         };
6271         
6272         var cm = this.cm;
6273         this.totalWidth = 0;
6274         
6275         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6276             
6277             var config = cm.config[i];
6278             
6279             var c = {
6280                 tag: 'th',
6281                 style : '',
6282                 html: cm.getColumnHeader(i)
6283             };
6284             
6285             var hh = '';
6286             
6287             if(typeof(config.sortable) != 'undefined' && config.sortable){
6288                 c.cls = 'sortable';
6289                 c.html = '<i class="glyphicon"></i>' + c.html;
6290             }
6291             
6292             if(typeof(config.lgHeader) != 'undefined'){
6293                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6294             }
6295             
6296             if(typeof(config.mdHeader) != 'undefined'){
6297                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6298             }
6299             
6300             if(typeof(config.smHeader) != 'undefined'){
6301                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6302             }
6303             
6304             if(typeof(config.xsHeader) != 'undefined'){
6305                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6306             }
6307             
6308             if(hh.length){
6309                 c.html = hh;
6310             }
6311             
6312             if(typeof(config.tooltip) != 'undefined'){
6313                 c.tooltip = config.tooltip;
6314             }
6315             
6316             if(typeof(config.colspan) != 'undefined'){
6317                 c.colspan = config.colspan;
6318             }
6319             
6320             if(typeof(config.hidden) != 'undefined' && config.hidden){
6321                 c.style += ' display:none;';
6322             }
6323             
6324             if(typeof(config.dataIndex) != 'undefined'){
6325                 c.sort = config.dataIndex;
6326             }
6327             
6328            
6329             
6330             if(typeof(config.align) != 'undefined' && config.align.length){
6331                 c.style += ' text-align:' + config.align + ';';
6332             }
6333             
6334             if(typeof(config.width) != 'undefined'){
6335                 c.style += ' width:' + config.width + 'px;';
6336                 this.totalWidth += config.width;
6337             } else {
6338                 this.totalWidth += 100; // assume minimum of 100 per column?
6339             }
6340             
6341             if(typeof(config.cls) != 'undefined'){
6342                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6343             }
6344             
6345             ['xs','sm','md','lg'].map(function(size){
6346                 
6347                 if(typeof(config[size]) == 'undefined'){
6348                     return;
6349                 }
6350                 
6351                 if (!config[size]) { // 0 = hidden
6352                     c.cls += ' hidden-' + size;
6353                     return;
6354                 }
6355                 
6356                 c.cls += ' col-' + size + '-' + config[size];
6357
6358             });
6359             
6360             header.cn.push(c)
6361         }
6362         
6363         return header;
6364     },
6365     
6366     renderBody : function()
6367     {
6368         var body = {
6369             tag: 'tbody',
6370             cn : [
6371                 {
6372                     tag: 'tr',
6373                     cn : [
6374                         {
6375                             tag : 'td',
6376                             colspan :  this.cm.getColumnCount()
6377                         }
6378                     ]
6379                 }
6380             ]
6381         };
6382         
6383         return body;
6384     },
6385     
6386     renderFooter : function()
6387     {
6388         var footer = {
6389             tag: 'tfoot',
6390             cn : [
6391                 {
6392                     tag: 'tr',
6393                     cn : [
6394                         {
6395                             tag : 'td',
6396                             colspan :  this.cm.getColumnCount()
6397                         }
6398                     ]
6399                 }
6400             ]
6401         };
6402         
6403         return footer;
6404     },
6405     
6406     
6407     
6408     onLoad : function()
6409     {
6410 //        Roo.log('ds onload');
6411         this.clear();
6412         
6413         var _this = this;
6414         var cm = this.cm;
6415         var ds = this.store;
6416         
6417         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6418             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6419             if (_this.store.sortInfo) {
6420                     
6421                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6422                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6423                 }
6424                 
6425                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6426                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6427                 }
6428             }
6429         });
6430         
6431         var tbody =  this.mainBody;
6432               
6433         if(ds.getCount() > 0){
6434             ds.data.each(function(d,rowIndex){
6435                 var row =  this.renderRow(cm, ds, rowIndex);
6436                 
6437                 tbody.createChild(row);
6438                 
6439                 var _this = this;
6440                 
6441                 if(row.cellObjects.length){
6442                     Roo.each(row.cellObjects, function(r){
6443                         _this.renderCellObject(r);
6444                     })
6445                 }
6446                 
6447             }, this);
6448         }
6449         
6450         Roo.each(this.el.select('tbody td', true).elements, function(e){
6451             e.on('mouseover', _this.onMouseover, _this);
6452         });
6453         
6454         Roo.each(this.el.select('tbody td', true).elements, function(e){
6455             e.on('mouseout', _this.onMouseout, _this);
6456         });
6457         this.fireEvent('rowsrendered', this);
6458         //if(this.loadMask){
6459         //    this.maskEl.hide();
6460         //}
6461         
6462         this.autoSize();
6463     },
6464     
6465     
6466     onUpdate : function(ds,record)
6467     {
6468         this.refreshRow(record);
6469         this.autoSize();
6470     },
6471     
6472     onRemove : function(ds, record, index, isUpdate){
6473         if(isUpdate !== true){
6474             this.fireEvent("beforerowremoved", this, index, record);
6475         }
6476         var bt = this.mainBody.dom;
6477         
6478         var rows = this.el.select('tbody > tr', true).elements;
6479         
6480         if(typeof(rows[index]) != 'undefined'){
6481             bt.removeChild(rows[index].dom);
6482         }
6483         
6484 //        if(bt.rows[index]){
6485 //            bt.removeChild(bt.rows[index]);
6486 //        }
6487         
6488         if(isUpdate !== true){
6489             //this.stripeRows(index);
6490             //this.syncRowHeights(index, index);
6491             //this.layout();
6492             this.fireEvent("rowremoved", this, index, record);
6493         }
6494     },
6495     
6496     onAdd : function(ds, records, rowIndex)
6497     {
6498         //Roo.log('on Add called');
6499         // - note this does not handle multiple adding very well..
6500         var bt = this.mainBody.dom;
6501         for (var i =0 ; i < records.length;i++) {
6502             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6503             //Roo.log(records[i]);
6504             //Roo.log(this.store.getAt(rowIndex+i));
6505             this.insertRow(this.store, rowIndex + i, false);
6506             return;
6507         }
6508         
6509     },
6510     
6511     
6512     refreshRow : function(record){
6513         var ds = this.store, index;
6514         if(typeof record == 'number'){
6515             index = record;
6516             record = ds.getAt(index);
6517         }else{
6518             index = ds.indexOf(record);
6519         }
6520         this.insertRow(ds, index, true);
6521         this.autoSize();
6522         this.onRemove(ds, record, index+1, true);
6523         this.autoSize();
6524         //this.syncRowHeights(index, index);
6525         //this.layout();
6526         this.fireEvent("rowupdated", this, index, record);
6527     },
6528     
6529     insertRow : function(dm, rowIndex, isUpdate){
6530         
6531         if(!isUpdate){
6532             this.fireEvent("beforerowsinserted", this, rowIndex);
6533         }
6534             //var s = this.getScrollState();
6535         var row = this.renderRow(this.cm, this.store, rowIndex);
6536         // insert before rowIndex..
6537         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6538         
6539         var _this = this;
6540                 
6541         if(row.cellObjects.length){
6542             Roo.each(row.cellObjects, function(r){
6543                 _this.renderCellObject(r);
6544             })
6545         }
6546             
6547         if(!isUpdate){
6548             this.fireEvent("rowsinserted", this, rowIndex);
6549             //this.syncRowHeights(firstRow, lastRow);
6550             //this.stripeRows(firstRow);
6551             //this.layout();
6552         }
6553         
6554     },
6555     
6556     
6557     getRowDom : function(rowIndex)
6558     {
6559         var rows = this.el.select('tbody > tr', true).elements;
6560         
6561         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6562         
6563     },
6564     // returns the object tree for a tr..
6565   
6566     
6567     renderRow : function(cm, ds, rowIndex) 
6568     {
6569         
6570         var d = ds.getAt(rowIndex);
6571         
6572         var row = {
6573             tag : 'tr',
6574             cn : []
6575         };
6576             
6577         var cellObjects = [];
6578         
6579         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6580             var config = cm.config[i];
6581             
6582             var renderer = cm.getRenderer(i);
6583             var value = '';
6584             var id = false;
6585             
6586             if(typeof(renderer) !== 'undefined'){
6587                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6588             }
6589             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6590             // and are rendered into the cells after the row is rendered - using the id for the element.
6591             
6592             if(typeof(value) === 'object'){
6593                 id = Roo.id();
6594                 cellObjects.push({
6595                     container : id,
6596                     cfg : value 
6597                 })
6598             }
6599             
6600             var rowcfg = {
6601                 record: d,
6602                 rowIndex : rowIndex,
6603                 colIndex : i,
6604                 rowClass : ''
6605             };
6606
6607             this.fireEvent('rowclass', this, rowcfg);
6608             
6609             var td = {
6610                 tag: 'td',
6611                 cls : rowcfg.rowClass,
6612                 style: '',
6613                 html: (typeof(value) === 'object') ? '' : value
6614             };
6615             
6616             if (id) {
6617                 td.id = id;
6618             }
6619             
6620             if(typeof(config.colspan) != 'undefined'){
6621                 td.colspan = config.colspan;
6622             }
6623             
6624             if(typeof(config.hidden) != 'undefined' && config.hidden){
6625                 td.style += ' display:none;';
6626             }
6627             
6628             if(typeof(config.align) != 'undefined' && config.align.length){
6629                 td.style += ' text-align:' + config.align + ';';
6630             }
6631             
6632             if(typeof(config.width) != 'undefined'){
6633                 td.style += ' width:' +  config.width + 'px;';
6634             }
6635             
6636             if(typeof(config.cursor) != 'undefined'){
6637                 td.style += ' cursor:' +  config.cursor + ';';
6638             }
6639             
6640             if(typeof(config.cls) != 'undefined'){
6641                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6642             }
6643             
6644             ['xs','sm','md','lg'].map(function(size){
6645                 
6646                 if(typeof(config[size]) == 'undefined'){
6647                     return;
6648                 }
6649                 
6650                 if (!config[size]) { // 0 = hidden
6651                     td.cls += ' hidden-' + size;
6652                     return;
6653                 }
6654                 
6655                 td.cls += ' col-' + size + '-' + config[size];
6656
6657             });
6658              
6659             row.cn.push(td);
6660            
6661         }
6662         
6663         row.cellObjects = cellObjects;
6664         
6665         return row;
6666           
6667     },
6668     
6669     
6670     
6671     onBeforeLoad : function()
6672     {
6673         //Roo.log('ds onBeforeLoad');
6674         
6675         //this.clear();
6676         
6677         //if(this.loadMask){
6678         //    this.maskEl.show();
6679         //}
6680     },
6681      /**
6682      * Remove all rows
6683      */
6684     clear : function()
6685     {
6686         this.el.select('tbody', true).first().dom.innerHTML = '';
6687     },
6688     /**
6689      * Show or hide a row.
6690      * @param {Number} rowIndex to show or hide
6691      * @param {Boolean} state hide
6692      */
6693     setRowVisibility : function(rowIndex, state)
6694     {
6695         var bt = this.mainBody.dom;
6696         
6697         var rows = this.el.select('tbody > tr', true).elements;
6698         
6699         if(typeof(rows[rowIndex]) == 'undefined'){
6700             return;
6701         }
6702         rows[rowIndex].dom.style.display = state ? '' : 'none';
6703     },
6704     
6705     
6706     getSelectionModel : function(){
6707         if(!this.selModel){
6708             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6709         }
6710         return this.selModel;
6711     },
6712     /*
6713      * Render the Roo.bootstrap object from renderder
6714      */
6715     renderCellObject : function(r)
6716     {
6717         var _this = this;
6718         
6719         var t = r.cfg.render(r.container);
6720         
6721         if(r.cfg.cn){
6722             Roo.each(r.cfg.cn, function(c){
6723                 var child = {
6724                     container: t.getChildContainer(),
6725                     cfg: c
6726                 };
6727                 _this.renderCellObject(child);
6728             })
6729         }
6730     },
6731     
6732     getRowIndex : function(row)
6733     {
6734         var rowIndex = -1;
6735         
6736         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6737             if(el != row){
6738                 return;
6739             }
6740             
6741             rowIndex = index;
6742         });
6743         
6744         return rowIndex;
6745     },
6746      /**
6747      * Returns the grid's underlying element = used by panel.Grid
6748      * @return {Element} The element
6749      */
6750     getGridEl : function(){
6751         return this.el;
6752     },
6753      /**
6754      * Forces a resize - used by panel.Grid
6755      * @return {Element} The element
6756      */
6757     autoSize : function()
6758     {
6759         //var ctr = Roo.get(this.container.dom.parentElement);
6760         var ctr = Roo.get(this.el.dom);
6761         
6762         var thd = this.getGridEl().select('thead',true).first();
6763         var tbd = this.getGridEl().select('tbody', true).first();
6764         var tfd = this.getGridEl().select('tfoot', true).first();
6765         
6766         var cw = ctr.getWidth();
6767         
6768         if (tbd) {
6769             
6770             tbd.setSize(ctr.getWidth(),
6771                         ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6772             );
6773             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6774             cw -= barsize;
6775         }
6776         cw = Math.max(cw, this.totalWidth);
6777         this.getGridEl().select('tr',true).setWidth(cw);
6778         // resize 'expandable coloumn?
6779         
6780         return; // we doe not have a view in this design..
6781         
6782     },
6783     onBodyScroll: function()
6784     {
6785         
6786         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6787         this.mainHead.setStyle({
6788                     'position' : 'relative',
6789                     'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6790         });
6791         
6792         
6793     }
6794 });
6795
6796  
6797
6798  /*
6799  * - LGPL
6800  *
6801  * table cell
6802  * 
6803  */
6804
6805 /**
6806  * @class Roo.bootstrap.TableCell
6807  * @extends Roo.bootstrap.Component
6808  * Bootstrap TableCell class
6809  * @cfg {String} html cell contain text
6810  * @cfg {String} cls cell class
6811  * @cfg {String} tag cell tag (td|th) default td
6812  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6813  * @cfg {String} align Aligns the content in a cell
6814  * @cfg {String} axis Categorizes cells
6815  * @cfg {String} bgcolor Specifies the background color of a cell
6816  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6817  * @cfg {Number} colspan Specifies the number of columns a cell should span
6818  * @cfg {String} headers Specifies one or more header cells a cell is related to
6819  * @cfg {Number} height Sets the height of a cell
6820  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6821  * @cfg {Number} rowspan Sets the number of rows a cell should span
6822  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6823  * @cfg {String} valign Vertical aligns the content in a cell
6824  * @cfg {Number} width Specifies the width of a cell
6825  * 
6826  * @constructor
6827  * Create a new TableCell
6828  * @param {Object} config The config object
6829  */
6830
6831 Roo.bootstrap.TableCell = function(config){
6832     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6833 };
6834
6835 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6836     
6837     html: false,
6838     cls: false,
6839     tag: false,
6840     abbr: false,
6841     align: false,
6842     axis: false,
6843     bgcolor: false,
6844     charoff: false,
6845     colspan: false,
6846     headers: false,
6847     height: false,
6848     nowrap: false,
6849     rowspan: false,
6850     scope: false,
6851     valign: false,
6852     width: false,
6853     
6854     
6855     getAutoCreate : function(){
6856         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6857         
6858         cfg = {
6859             tag: 'td'
6860         };
6861         
6862         if(this.tag){
6863             cfg.tag = this.tag;
6864         }
6865         
6866         if (this.html) {
6867             cfg.html=this.html
6868         }
6869         if (this.cls) {
6870             cfg.cls=this.cls
6871         }
6872         if (this.abbr) {
6873             cfg.abbr=this.abbr
6874         }
6875         if (this.align) {
6876             cfg.align=this.align
6877         }
6878         if (this.axis) {
6879             cfg.axis=this.axis
6880         }
6881         if (this.bgcolor) {
6882             cfg.bgcolor=this.bgcolor
6883         }
6884         if (this.charoff) {
6885             cfg.charoff=this.charoff
6886         }
6887         if (this.colspan) {
6888             cfg.colspan=this.colspan
6889         }
6890         if (this.headers) {
6891             cfg.headers=this.headers
6892         }
6893         if (this.height) {
6894             cfg.height=this.height
6895         }
6896         if (this.nowrap) {
6897             cfg.nowrap=this.nowrap
6898         }
6899         if (this.rowspan) {
6900             cfg.rowspan=this.rowspan
6901         }
6902         if (this.scope) {
6903             cfg.scope=this.scope
6904         }
6905         if (this.valign) {
6906             cfg.valign=this.valign
6907         }
6908         if (this.width) {
6909             cfg.width=this.width
6910         }
6911         
6912         
6913         return cfg;
6914     }
6915    
6916 });
6917
6918  
6919
6920  /*
6921  * - LGPL
6922  *
6923  * table row
6924  * 
6925  */
6926
6927 /**
6928  * @class Roo.bootstrap.TableRow
6929  * @extends Roo.bootstrap.Component
6930  * Bootstrap TableRow class
6931  * @cfg {String} cls row class
6932  * @cfg {String} align Aligns the content in a table row
6933  * @cfg {String} bgcolor Specifies a background color for a table row
6934  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6935  * @cfg {String} valign Vertical aligns the content in a table row
6936  * 
6937  * @constructor
6938  * Create a new TableRow
6939  * @param {Object} config The config object
6940  */
6941
6942 Roo.bootstrap.TableRow = function(config){
6943     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6944 };
6945
6946 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6947     
6948     cls: false,
6949     align: false,
6950     bgcolor: false,
6951     charoff: false,
6952     valign: false,
6953     
6954     getAutoCreate : function(){
6955         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6956         
6957         cfg = {
6958             tag: 'tr'
6959         };
6960             
6961         if(this.cls){
6962             cfg.cls = this.cls;
6963         }
6964         if(this.align){
6965             cfg.align = this.align;
6966         }
6967         if(this.bgcolor){
6968             cfg.bgcolor = this.bgcolor;
6969         }
6970         if(this.charoff){
6971             cfg.charoff = this.charoff;
6972         }
6973         if(this.valign){
6974             cfg.valign = this.valign;
6975         }
6976         
6977         return cfg;
6978     }
6979    
6980 });
6981
6982  
6983
6984  /*
6985  * - LGPL
6986  *
6987  * table body
6988  * 
6989  */
6990
6991 /**
6992  * @class Roo.bootstrap.TableBody
6993  * @extends Roo.bootstrap.Component
6994  * Bootstrap TableBody class
6995  * @cfg {String} cls element class
6996  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6997  * @cfg {String} align Aligns the content inside the element
6998  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6999  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7000  * 
7001  * @constructor
7002  * Create a new TableBody
7003  * @param {Object} config The config object
7004  */
7005
7006 Roo.bootstrap.TableBody = function(config){
7007     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7008 };
7009
7010 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7011     
7012     cls: false,
7013     tag: false,
7014     align: false,
7015     charoff: false,
7016     valign: false,
7017     
7018     getAutoCreate : function(){
7019         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7020         
7021         cfg = {
7022             tag: 'tbody'
7023         };
7024             
7025         if (this.cls) {
7026             cfg.cls=this.cls
7027         }
7028         if(this.tag){
7029             cfg.tag = this.tag;
7030         }
7031         
7032         if(this.align){
7033             cfg.align = this.align;
7034         }
7035         if(this.charoff){
7036             cfg.charoff = this.charoff;
7037         }
7038         if(this.valign){
7039             cfg.valign = this.valign;
7040         }
7041         
7042         return cfg;
7043     }
7044     
7045     
7046 //    initEvents : function()
7047 //    {
7048 //        
7049 //        if(!this.store){
7050 //            return;
7051 //        }
7052 //        
7053 //        this.store = Roo.factory(this.store, Roo.data);
7054 //        this.store.on('load', this.onLoad, this);
7055 //        
7056 //        this.store.load();
7057 //        
7058 //    },
7059 //    
7060 //    onLoad: function () 
7061 //    {   
7062 //        this.fireEvent('load', this);
7063 //    }
7064 //    
7065 //   
7066 });
7067
7068  
7069
7070  /*
7071  * Based on:
7072  * Ext JS Library 1.1.1
7073  * Copyright(c) 2006-2007, Ext JS, LLC.
7074  *
7075  * Originally Released Under LGPL - original licence link has changed is not relivant.
7076  *
7077  * Fork - LGPL
7078  * <script type="text/javascript">
7079  */
7080
7081 // as we use this in bootstrap.
7082 Roo.namespace('Roo.form');
7083  /**
7084  * @class Roo.form.Action
7085  * Internal Class used to handle form actions
7086  * @constructor
7087  * @param {Roo.form.BasicForm} el The form element or its id
7088  * @param {Object} config Configuration options
7089  */
7090
7091  
7092  
7093 // define the action interface
7094 Roo.form.Action = function(form, options){
7095     this.form = form;
7096     this.options = options || {};
7097 };
7098 /**
7099  * Client Validation Failed
7100  * @const 
7101  */
7102 Roo.form.Action.CLIENT_INVALID = 'client';
7103 /**
7104  * Server Validation Failed
7105  * @const 
7106  */
7107 Roo.form.Action.SERVER_INVALID = 'server';
7108  /**
7109  * Connect to Server Failed
7110  * @const 
7111  */
7112 Roo.form.Action.CONNECT_FAILURE = 'connect';
7113 /**
7114  * Reading Data from Server Failed
7115  * @const 
7116  */
7117 Roo.form.Action.LOAD_FAILURE = 'load';
7118
7119 Roo.form.Action.prototype = {
7120     type : 'default',
7121     failureType : undefined,
7122     response : undefined,
7123     result : undefined,
7124
7125     // interface method
7126     run : function(options){
7127
7128     },
7129
7130     // interface method
7131     success : function(response){
7132
7133     },
7134
7135     // interface method
7136     handleResponse : function(response){
7137
7138     },
7139
7140     // default connection failure
7141     failure : function(response){
7142         
7143         this.response = response;
7144         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7145         this.form.afterAction(this, false);
7146     },
7147
7148     processResponse : function(response){
7149         this.response = response;
7150         if(!response.responseText){
7151             return true;
7152         }
7153         this.result = this.handleResponse(response);
7154         return this.result;
7155     },
7156
7157     // utility functions used internally
7158     getUrl : function(appendParams){
7159         var url = this.options.url || this.form.url || this.form.el.dom.action;
7160         if(appendParams){
7161             var p = this.getParams();
7162             if(p){
7163                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7164             }
7165         }
7166         return url;
7167     },
7168
7169     getMethod : function(){
7170         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7171     },
7172
7173     getParams : function(){
7174         var bp = this.form.baseParams;
7175         var p = this.options.params;
7176         if(p){
7177             if(typeof p == "object"){
7178                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7179             }else if(typeof p == 'string' && bp){
7180                 p += '&' + Roo.urlEncode(bp);
7181             }
7182         }else if(bp){
7183             p = Roo.urlEncode(bp);
7184         }
7185         return p;
7186     },
7187
7188     createCallback : function(){
7189         return {
7190             success: this.success,
7191             failure: this.failure,
7192             scope: this,
7193             timeout: (this.form.timeout*1000),
7194             upload: this.form.fileUpload ? this.success : undefined
7195         };
7196     }
7197 };
7198
7199 Roo.form.Action.Submit = function(form, options){
7200     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7201 };
7202
7203 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7204     type : 'submit',
7205
7206     haveProgress : false,
7207     uploadComplete : false,
7208     
7209     // uploadProgress indicator.
7210     uploadProgress : function()
7211     {
7212         if (!this.form.progressUrl) {
7213             return;
7214         }
7215         
7216         if (!this.haveProgress) {
7217             Roo.MessageBox.progress("Uploading", "Uploading");
7218         }
7219         if (this.uploadComplete) {
7220            Roo.MessageBox.hide();
7221            return;
7222         }
7223         
7224         this.haveProgress = true;
7225    
7226         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7227         
7228         var c = new Roo.data.Connection();
7229         c.request({
7230             url : this.form.progressUrl,
7231             params: {
7232                 id : uid
7233             },
7234             method: 'GET',
7235             success : function(req){
7236                //console.log(data);
7237                 var rdata = false;
7238                 var edata;
7239                 try  {
7240                    rdata = Roo.decode(req.responseText)
7241                 } catch (e) {
7242                     Roo.log("Invalid data from server..");
7243                     Roo.log(edata);
7244                     return;
7245                 }
7246                 if (!rdata || !rdata.success) {
7247                     Roo.log(rdata);
7248                     Roo.MessageBox.alert(Roo.encode(rdata));
7249                     return;
7250                 }
7251                 var data = rdata.data;
7252                 
7253                 if (this.uploadComplete) {
7254                    Roo.MessageBox.hide();
7255                    return;
7256                 }
7257                    
7258                 if (data){
7259                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7260                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7261                     );
7262                 }
7263                 this.uploadProgress.defer(2000,this);
7264             },
7265        
7266             failure: function(data) {
7267                 Roo.log('progress url failed ');
7268                 Roo.log(data);
7269             },
7270             scope : this
7271         });
7272            
7273     },
7274     
7275     
7276     run : function()
7277     {
7278         // run get Values on the form, so it syncs any secondary forms.
7279         this.form.getValues();
7280         
7281         var o = this.options;
7282         var method = this.getMethod();
7283         var isPost = method == 'POST';
7284         if(o.clientValidation === false || this.form.isValid()){
7285             
7286             if (this.form.progressUrl) {
7287                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7288                     (new Date() * 1) + '' + Math.random());
7289                     
7290             } 
7291             
7292             
7293             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7294                 form:this.form.el.dom,
7295                 url:this.getUrl(!isPost),
7296                 method: method,
7297                 params:isPost ? this.getParams() : null,
7298                 isUpload: this.form.fileUpload
7299             }));
7300             
7301             this.uploadProgress();
7302
7303         }else if (o.clientValidation !== false){ // client validation failed
7304             this.failureType = Roo.form.Action.CLIENT_INVALID;
7305             this.form.afterAction(this, false);
7306         }
7307     },
7308
7309     success : function(response)
7310     {
7311         this.uploadComplete= true;
7312         if (this.haveProgress) {
7313             Roo.MessageBox.hide();
7314         }
7315         
7316         
7317         var result = this.processResponse(response);
7318         if(result === true || result.success){
7319             this.form.afterAction(this, true);
7320             return;
7321         }
7322         if(result.errors){
7323             this.form.markInvalid(result.errors);
7324             this.failureType = Roo.form.Action.SERVER_INVALID;
7325         }
7326         this.form.afterAction(this, false);
7327     },
7328     failure : function(response)
7329     {
7330         this.uploadComplete= true;
7331         if (this.haveProgress) {
7332             Roo.MessageBox.hide();
7333         }
7334         
7335         this.response = response;
7336         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7337         this.form.afterAction(this, false);
7338     },
7339     
7340     handleResponse : function(response){
7341         if(this.form.errorReader){
7342             var rs = this.form.errorReader.read(response);
7343             var errors = [];
7344             if(rs.records){
7345                 for(var i = 0, len = rs.records.length; i < len; i++) {
7346                     var r = rs.records[i];
7347                     errors[i] = r.data;
7348                 }
7349             }
7350             if(errors.length < 1){
7351                 errors = null;
7352             }
7353             return {
7354                 success : rs.success,
7355                 errors : errors
7356             };
7357         }
7358         var ret = false;
7359         try {
7360             ret = Roo.decode(response.responseText);
7361         } catch (e) {
7362             ret = {
7363                 success: false,
7364                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7365                 errors : []
7366             };
7367         }
7368         return ret;
7369         
7370     }
7371 });
7372
7373
7374 Roo.form.Action.Load = function(form, options){
7375     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7376     this.reader = this.form.reader;
7377 };
7378
7379 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7380     type : 'load',
7381
7382     run : function(){
7383         
7384         Roo.Ajax.request(Roo.apply(
7385                 this.createCallback(), {
7386                     method:this.getMethod(),
7387                     url:this.getUrl(false),
7388                     params:this.getParams()
7389         }));
7390     },
7391
7392     success : function(response){
7393         
7394         var result = this.processResponse(response);
7395         if(result === true || !result.success || !result.data){
7396             this.failureType = Roo.form.Action.LOAD_FAILURE;
7397             this.form.afterAction(this, false);
7398             return;
7399         }
7400         this.form.clearInvalid();
7401         this.form.setValues(result.data);
7402         this.form.afterAction(this, true);
7403     },
7404
7405     handleResponse : function(response){
7406         if(this.form.reader){
7407             var rs = this.form.reader.read(response);
7408             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7409             return {
7410                 success : rs.success,
7411                 data : data
7412             };
7413         }
7414         return Roo.decode(response.responseText);
7415     }
7416 });
7417
7418 Roo.form.Action.ACTION_TYPES = {
7419     'load' : Roo.form.Action.Load,
7420     'submit' : Roo.form.Action.Submit
7421 };/*
7422  * - LGPL
7423  *
7424  * form
7425  *
7426  */
7427
7428 /**
7429  * @class Roo.bootstrap.Form
7430  * @extends Roo.bootstrap.Component
7431  * Bootstrap Form class
7432  * @cfg {String} method  GET | POST (default POST)
7433  * @cfg {String} labelAlign top | left (default top)
7434  * @cfg {String} align left  | right - for navbars
7435  * @cfg {Boolean} loadMask load mask when submit (default true)
7436
7437  *
7438  * @constructor
7439  * Create a new Form
7440  * @param {Object} config The config object
7441  */
7442
7443
7444 Roo.bootstrap.Form = function(config){
7445     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7446     
7447     Roo.bootstrap.Form.popover.apply();
7448     
7449     this.addEvents({
7450         /**
7451          * @event clientvalidation
7452          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7453          * @param {Form} this
7454          * @param {Boolean} valid true if the form has passed client-side validation
7455          */
7456         clientvalidation: true,
7457         /**
7458          * @event beforeaction
7459          * Fires before any action is performed. Return false to cancel the action.
7460          * @param {Form} this
7461          * @param {Action} action The action to be performed
7462          */
7463         beforeaction: true,
7464         /**
7465          * @event actionfailed
7466          * Fires when an action fails.
7467          * @param {Form} this
7468          * @param {Action} action The action that failed
7469          */
7470         actionfailed : true,
7471         /**
7472          * @event actioncomplete
7473          * Fires when an action is completed.
7474          * @param {Form} this
7475          * @param {Action} action The action that completed
7476          */
7477         actioncomplete : true
7478     });
7479
7480 };
7481
7482 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7483
7484      /**
7485      * @cfg {String} method
7486      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7487      */
7488     method : 'POST',
7489     /**
7490      * @cfg {String} url
7491      * The URL to use for form actions if one isn't supplied in the action options.
7492      */
7493     /**
7494      * @cfg {Boolean} fileUpload
7495      * Set to true if this form is a file upload.
7496      */
7497
7498     /**
7499      * @cfg {Object} baseParams
7500      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7501      */
7502
7503     /**
7504      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7505      */
7506     timeout: 30,
7507     /**
7508      * @cfg {Sting} align (left|right) for navbar forms
7509      */
7510     align : 'left',
7511
7512     // private
7513     activeAction : null,
7514
7515     /**
7516      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7517      * element by passing it or its id or mask the form itself by passing in true.
7518      * @type Mixed
7519      */
7520     waitMsgTarget : false,
7521
7522     loadMask : true,
7523     
7524     /**
7525      * @cfg {Boolean} errPopover (true|false) default false
7526      */
7527     errPopover : false,
7528
7529     getAutoCreate : function(){
7530
7531         var cfg = {
7532             tag: 'form',
7533             method : this.method || 'POST',
7534             id : this.id || Roo.id(),
7535             cls : ''
7536         };
7537         if (this.parent().xtype.match(/^Nav/)) {
7538             cfg.cls = 'navbar-form navbar-' + this.align;
7539
7540         }
7541
7542         if (this.labelAlign == 'left' ) {
7543             cfg.cls += ' form-horizontal';
7544         }
7545
7546
7547         return cfg;
7548     },
7549     initEvents : function()
7550     {
7551         this.el.on('submit', this.onSubmit, this);
7552         // this was added as random key presses on the form where triggering form submit.
7553         this.el.on('keypress', function(e) {
7554             if (e.getCharCode() != 13) {
7555                 return true;
7556             }
7557             // we might need to allow it for textareas.. and some other items.
7558             // check e.getTarget().
7559
7560             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7561                 return true;
7562             }
7563
7564             Roo.log("keypress blocked");
7565
7566             e.preventDefault();
7567             return false;
7568         });
7569         
7570     },
7571     // private
7572     onSubmit : function(e){
7573         e.stopEvent();
7574     },
7575
7576      /**
7577      * Returns true if client-side validation on the form is successful.
7578      * @return Boolean
7579      */
7580     isValid : function(){
7581         var items = this.getItems();
7582         var valid = true;
7583         var target = false;
7584         
7585         items.each(function(f){
7586             
7587             if(f.validate()){
7588                 return;
7589             }
7590             
7591             valid = false;
7592
7593             if(!target && f.el.isVisible(true)){
7594                 target = f;
7595             }
7596            
7597         });
7598         
7599         if(this.errPopover && !valid){
7600             Roo.bootstrap.Form.popover.mask(this, target);
7601         }
7602         
7603         return valid;
7604     },
7605     
7606     /**
7607      * Returns true if any fields in this form have changed since their original load.
7608      * @return Boolean
7609      */
7610     isDirty : function(){
7611         var dirty = false;
7612         var items = this.getItems();
7613         items.each(function(f){
7614            if(f.isDirty()){
7615                dirty = true;
7616                return false;
7617            }
7618            return true;
7619         });
7620         return dirty;
7621     },
7622      /**
7623      * Performs a predefined action (submit or load) or custom actions you define on this form.
7624      * @param {String} actionName The name of the action type
7625      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7626      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7627      * accept other config options):
7628      * <pre>
7629 Property          Type             Description
7630 ----------------  ---------------  ----------------------------------------------------------------------------------
7631 url               String           The url for the action (defaults to the form's url)
7632 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7633 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7634 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7635                                    validate the form on the client (defaults to false)
7636      * </pre>
7637      * @return {BasicForm} this
7638      */
7639     doAction : function(action, options){
7640         if(typeof action == 'string'){
7641             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7642         }
7643         if(this.fireEvent('beforeaction', this, action) !== false){
7644             this.beforeAction(action);
7645             action.run.defer(100, action);
7646         }
7647         return this;
7648     },
7649
7650     // private
7651     beforeAction : function(action){
7652         var o = action.options;
7653
7654         if(this.loadMask){
7655             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7656         }
7657         // not really supported yet.. ??
7658
7659         //if(this.waitMsgTarget === true){
7660         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7661         //}else if(this.waitMsgTarget){
7662         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7663         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7664         //}else {
7665         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7666        // }
7667
7668     },
7669
7670     // private
7671     afterAction : function(action, success){
7672         this.activeAction = null;
7673         var o = action.options;
7674
7675         //if(this.waitMsgTarget === true){
7676             this.el.unmask();
7677         //}else if(this.waitMsgTarget){
7678         //    this.waitMsgTarget.unmask();
7679         //}else{
7680         //    Roo.MessageBox.updateProgress(1);
7681         //    Roo.MessageBox.hide();
7682        // }
7683         //
7684         if(success){
7685             if(o.reset){
7686                 this.reset();
7687             }
7688             Roo.callback(o.success, o.scope, [this, action]);
7689             this.fireEvent('actioncomplete', this, action);
7690
7691         }else{
7692
7693             // failure condition..
7694             // we have a scenario where updates need confirming.
7695             // eg. if a locking scenario exists..
7696             // we look for { errors : { needs_confirm : true }} in the response.
7697             if (
7698                 (typeof(action.result) != 'undefined')  &&
7699                 (typeof(action.result.errors) != 'undefined')  &&
7700                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7701            ){
7702                 var _t = this;
7703                 Roo.log("not supported yet");
7704                  /*
7705
7706                 Roo.MessageBox.confirm(
7707                     "Change requires confirmation",
7708                     action.result.errorMsg,
7709                     function(r) {
7710                         if (r != 'yes') {
7711                             return;
7712                         }
7713                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7714                     }
7715
7716                 );
7717                 */
7718
7719
7720                 return;
7721             }
7722
7723             Roo.callback(o.failure, o.scope, [this, action]);
7724             // show an error message if no failed handler is set..
7725             if (!this.hasListener('actionfailed')) {
7726                 Roo.log("need to add dialog support");
7727                 /*
7728                 Roo.MessageBox.alert("Error",
7729                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7730                         action.result.errorMsg :
7731                         "Saving Failed, please check your entries or try again"
7732                 );
7733                 */
7734             }
7735
7736             this.fireEvent('actionfailed', this, action);
7737         }
7738
7739     },
7740     /**
7741      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7742      * @param {String} id The value to search for
7743      * @return Field
7744      */
7745     findField : function(id){
7746         var items = this.getItems();
7747         var field = items.get(id);
7748         if(!field){
7749              items.each(function(f){
7750                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7751                     field = f;
7752                     return false;
7753                 }
7754                 return true;
7755             });
7756         }
7757         return field || null;
7758     },
7759      /**
7760      * Mark fields in this form invalid in bulk.
7761      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7762      * @return {BasicForm} this
7763      */
7764     markInvalid : function(errors){
7765         if(errors instanceof Array){
7766             for(var i = 0, len = errors.length; i < len; i++){
7767                 var fieldError = errors[i];
7768                 var f = this.findField(fieldError.id);
7769                 if(f){
7770                     f.markInvalid(fieldError.msg);
7771                 }
7772             }
7773         }else{
7774             var field, id;
7775             for(id in errors){
7776                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7777                     field.markInvalid(errors[id]);
7778                 }
7779             }
7780         }
7781         //Roo.each(this.childForms || [], function (f) {
7782         //    f.markInvalid(errors);
7783         //});
7784
7785         return this;
7786     },
7787
7788     /**
7789      * Set values for fields in this form in bulk.
7790      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7791      * @return {BasicForm} this
7792      */
7793     setValues : function(values){
7794         if(values instanceof Array){ // array of objects
7795             for(var i = 0, len = values.length; i < len; i++){
7796                 var v = values[i];
7797                 var f = this.findField(v.id);
7798                 if(f){
7799                     f.setValue(v.value);
7800                     if(this.trackResetOnLoad){
7801                         f.originalValue = f.getValue();
7802                     }
7803                 }
7804             }
7805         }else{ // object hash
7806             var field, id;
7807             for(id in values){
7808                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7809
7810                     if (field.setFromData &&
7811                         field.valueField &&
7812                         field.displayField &&
7813                         // combos' with local stores can
7814                         // be queried via setValue()
7815                         // to set their value..
7816                         (field.store && !field.store.isLocal)
7817                         ) {
7818                         // it's a combo
7819                         var sd = { };
7820                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7821                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7822                         field.setFromData(sd);
7823
7824                     } else {
7825                         field.setValue(values[id]);
7826                     }
7827
7828
7829                     if(this.trackResetOnLoad){
7830                         field.originalValue = field.getValue();
7831                     }
7832                 }
7833             }
7834         }
7835
7836         //Roo.each(this.childForms || [], function (f) {
7837         //    f.setValues(values);
7838         //});
7839
7840         return this;
7841     },
7842
7843     /**
7844      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7845      * they are returned as an array.
7846      * @param {Boolean} asString
7847      * @return {Object}
7848      */
7849     getValues : function(asString){
7850         //if (this.childForms) {
7851             // copy values from the child forms
7852         //    Roo.each(this.childForms, function (f) {
7853         //        this.setValues(f.getValues());
7854         //    }, this);
7855         //}
7856
7857
7858
7859         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7860         if(asString === true){
7861             return fs;
7862         }
7863         return Roo.urlDecode(fs);
7864     },
7865
7866     /**
7867      * Returns the fields in this form as an object with key/value pairs.
7868      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7869      * @return {Object}
7870      */
7871     getFieldValues : function(with_hidden)
7872     {
7873         var items = this.getItems();
7874         var ret = {};
7875         items.each(function(f){
7876             if (!f.getName()) {
7877                 return;
7878             }
7879             var v = f.getValue();
7880             if (f.inputType =='radio') {
7881                 if (typeof(ret[f.getName()]) == 'undefined') {
7882                     ret[f.getName()] = ''; // empty..
7883                 }
7884
7885                 if (!f.el.dom.checked) {
7886                     return;
7887
7888                 }
7889                 v = f.el.dom.value;
7890
7891             }
7892
7893             // not sure if this supported any more..
7894             if ((typeof(v) == 'object') && f.getRawValue) {
7895                 v = f.getRawValue() ; // dates..
7896             }
7897             // combo boxes where name != hiddenName...
7898             if (f.name !== false && f.name != '' && f.name != f.getName()) {
7899                 ret[f.name] = f.getRawValue();
7900             }
7901             ret[f.getName()] = v;
7902         });
7903
7904         return ret;
7905     },
7906
7907     /**
7908      * Clears all invalid messages in this form.
7909      * @return {BasicForm} this
7910      */
7911     clearInvalid : function(){
7912         var items = this.getItems();
7913
7914         items.each(function(f){
7915            f.clearInvalid();
7916         });
7917
7918
7919
7920         return this;
7921     },
7922
7923     /**
7924      * Resets this form.
7925      * @return {BasicForm} this
7926      */
7927     reset : function(){
7928         var items = this.getItems();
7929         items.each(function(f){
7930             f.reset();
7931         });
7932
7933         Roo.each(this.childForms || [], function (f) {
7934             f.reset();
7935         });
7936
7937
7938         return this;
7939     },
7940     getItems : function()
7941     {
7942         var r=new Roo.util.MixedCollection(false, function(o){
7943             return o.id || (o.id = Roo.id());
7944         });
7945         var iter = function(el) {
7946             if (el.inputEl) {
7947                 r.add(el);
7948             }
7949             if (!el.items) {
7950                 return;
7951             }
7952             Roo.each(el.items,function(e) {
7953                 iter(e);
7954             });
7955
7956
7957         };
7958
7959         iter(this);
7960         return r;
7961
7962
7963
7964
7965     }
7966
7967 });
7968
7969 Roo.apply(Roo.bootstrap.Form, {
7970     
7971     popover : {
7972         
7973         isApplied : false,
7974         
7975         isMasked : false,
7976         
7977         form : false,
7978         
7979         target : false,
7980         
7981         oIndex : false,
7982         
7983         toolTip : false,
7984         
7985         intervalID : false,
7986     
7987         apply : function()
7988         {
7989             if(this.isApplied){
7990                 return;
7991             }
7992             
7993             this.toolTip = new Roo.bootstrap.Tooltip({
7994                 cls : 'roo-form-error-popover',
7995                 alignment : {
7996                     'left' : ['r-l', [-2,0], 'right'],
7997                     'right' : ['l-r', [2,0], 'left'],
7998                     'bottom' : ['tl-bl', [0,2], 'top'],
7999                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8000                 }
8001             });
8002             
8003             this.toolTip.render(Roo.get(document.body));
8004
8005             this.toolTip.el.setVisibilityMode(Roo.Element.DISPLAY);
8006             
8007             Roo.get(document.body).on('click', function(){
8008                 this.unmask();
8009             }, this);
8010             
8011             this.isApplied = true
8012         },
8013         
8014         mask : function(form, target)
8015         {
8016             this.form = form;
8017             
8018             this.target = target;
8019             
8020             if(!this.form.errPopover){
8021                 return;
8022             }
8023
8024             this.oIndex = target.el.getStyle('z-index');
8025             
8026             this.target.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8027         
8028             this.target.el.addClass('roo-invalid-outline');
8029             
8030             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8031             
8032             var scrolled = scrollable.getScroll();
8033             
8034             var ot = this.target.el.calcOffsetsTo(scrollable);
8035             
8036             var scrollTo = 0;
8037             
8038             if(ot[1] <= scrolled.top){
8039                 scrollTo = ot[1] - 100;
8040             } else {
8041                 scrollTo = ot[1] + Roo.lib.Dom.getViewHeight() - 100;
8042             }
8043             
8044             scrollable.scrollTo('top', scrollTo);
8045             
8046             this.toolTip.bindEl = this.target.el;
8047         
8048             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8049
8050             var tip = this.target.blankText;
8051
8052             if(this.target.getValue() !== '' && this.target.regexText.length){
8053                 tip = this.target.regexText;
8054             }
8055
8056             this.toolTip.show(tip);
8057             
8058             this.intervalID = window.setInterval(function() {
8059                 Roo.bootstrap.Form.popover.unmask();
8060             }, 10000);
8061
8062             window.onwheel = function(){ return false;};
8063             
8064             (function(){ this.isMasked = true; }).defer(500, this);
8065             
8066         },
8067         
8068         unmask : function()
8069         {
8070             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errPopover){
8071                 return;
8072             }
8073             
8074             if(this.oIndex){
8075                 this.target.el.setStyle('z-index', this.oIndex);
8076             }
8077             
8078             this.target.el.removeClass('roo-invalid-outline');
8079             
8080             this.toolTip.hide();
8081             
8082             this.toolTip.el.hide();
8083             
8084             window.onwheel = function(){ return true;};
8085             
8086             if(this.intervalID){
8087                 window.clearInterval(this.intervalID);
8088                 this.intervalID = false;
8089             }
8090             
8091             this.isMasked = false;
8092             
8093         }
8094         
8095     }
8096     
8097 });
8098
8099 /*
8100  * Based on:
8101  * Ext JS Library 1.1.1
8102  * Copyright(c) 2006-2007, Ext JS, LLC.
8103  *
8104  * Originally Released Under LGPL - original licence link has changed is not relivant.
8105  *
8106  * Fork - LGPL
8107  * <script type="text/javascript">
8108  */
8109 /**
8110  * @class Roo.form.VTypes
8111  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8112  * @singleton
8113  */
8114 Roo.form.VTypes = function(){
8115     // closure these in so they are only created once.
8116     var alpha = /^[a-zA-Z_]+$/;
8117     var alphanum = /^[a-zA-Z0-9_]+$/;
8118     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8119     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8120
8121     // All these messages and functions are configurable
8122     return {
8123         /**
8124          * The function used to validate email addresses
8125          * @param {String} value The email address
8126          */
8127         'email' : function(v){
8128             return email.test(v);
8129         },
8130         /**
8131          * The error text to display when the email validation function returns false
8132          * @type String
8133          */
8134         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8135         /**
8136          * The keystroke filter mask to be applied on email input
8137          * @type RegExp
8138          */
8139         'emailMask' : /[a-z0-9_\.\-@]/i,
8140
8141         /**
8142          * The function used to validate URLs
8143          * @param {String} value The URL
8144          */
8145         'url' : function(v){
8146             return url.test(v);
8147         },
8148         /**
8149          * The error text to display when the url validation function returns false
8150          * @type String
8151          */
8152         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8153         
8154         /**
8155          * The function used to validate alpha values
8156          * @param {String} value The value
8157          */
8158         'alpha' : function(v){
8159             return alpha.test(v);
8160         },
8161         /**
8162          * The error text to display when the alpha validation function returns false
8163          * @type String
8164          */
8165         'alphaText' : 'This field should only contain letters and _',
8166         /**
8167          * The keystroke filter mask to be applied on alpha input
8168          * @type RegExp
8169          */
8170         'alphaMask' : /[a-z_]/i,
8171
8172         /**
8173          * The function used to validate alphanumeric values
8174          * @param {String} value The value
8175          */
8176         'alphanum' : function(v){
8177             return alphanum.test(v);
8178         },
8179         /**
8180          * The error text to display when the alphanumeric validation function returns false
8181          * @type String
8182          */
8183         'alphanumText' : 'This field should only contain letters, numbers and _',
8184         /**
8185          * The keystroke filter mask to be applied on alphanumeric input
8186          * @type RegExp
8187          */
8188         'alphanumMask' : /[a-z0-9_]/i
8189     };
8190 }();/*
8191  * - LGPL
8192  *
8193  * Input
8194  * 
8195  */
8196
8197 /**
8198  * @class Roo.bootstrap.Input
8199  * @extends Roo.bootstrap.Component
8200  * Bootstrap Input class
8201  * @cfg {Boolean} disabled is it disabled
8202  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8203  * @cfg {String} name name of the input
8204  * @cfg {string} fieldLabel - the label associated
8205  * @cfg {string} placeholder - placeholder to put in text.
8206  * @cfg {string}  before - input group add on before
8207  * @cfg {string} after - input group add on after
8208  * @cfg {string} size - (lg|sm) or leave empty..
8209  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8210  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8211  * @cfg {Number} md colspan out of 12 for computer-sized screens
8212  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8213  * @cfg {string} value default value of the input
8214  * @cfg {Number} labelWidth set the width of label 
8215  * @cfg {Number} labellg set the width of label (1-12)
8216  * @cfg {Number} labelmd set the width of label (1-12)
8217  * @cfg {Number} labelsm set the width of label (1-12)
8218  * @cfg {Number} labelxs set the width of label (1-12)
8219  * @cfg {String} labelAlign (top|left)
8220  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8221  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8222  * @cfg {String} indicatorpos (left|right) default left
8223
8224  * @cfg {String} align (left|center|right) Default left
8225  * @cfg {Boolean} forceFeedback (true|false) Default false
8226  * 
8227  * 
8228  * 
8229  * 
8230  * @constructor
8231  * Create a new Input
8232  * @param {Object} config The config object
8233  */
8234
8235 Roo.bootstrap.Input = function(config){
8236     
8237     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8238     
8239     this.addEvents({
8240         /**
8241          * @event focus
8242          * Fires when this field receives input focus.
8243          * @param {Roo.form.Field} this
8244          */
8245         focus : true,
8246         /**
8247          * @event blur
8248          * Fires when this field loses input focus.
8249          * @param {Roo.form.Field} this
8250          */
8251         blur : true,
8252         /**
8253          * @event specialkey
8254          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8255          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8256          * @param {Roo.form.Field} this
8257          * @param {Roo.EventObject} e The event object
8258          */
8259         specialkey : true,
8260         /**
8261          * @event change
8262          * Fires just before the field blurs if the field value has changed.
8263          * @param {Roo.form.Field} this
8264          * @param {Mixed} newValue The new value
8265          * @param {Mixed} oldValue The original value
8266          */
8267         change : true,
8268         /**
8269          * @event invalid
8270          * Fires after the field has been marked as invalid.
8271          * @param {Roo.form.Field} this
8272          * @param {String} msg The validation message
8273          */
8274         invalid : true,
8275         /**
8276          * @event valid
8277          * Fires after the field has been validated with no errors.
8278          * @param {Roo.form.Field} this
8279          */
8280         valid : true,
8281          /**
8282          * @event keyup
8283          * Fires after the key up
8284          * @param {Roo.form.Field} this
8285          * @param {Roo.EventObject}  e The event Object
8286          */
8287         keyup : true
8288     });
8289 };
8290
8291 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8292      /**
8293      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8294       automatic validation (defaults to "keyup").
8295      */
8296     validationEvent : "keyup",
8297      /**
8298      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8299      */
8300     validateOnBlur : true,
8301     /**
8302      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8303      */
8304     validationDelay : 250,
8305      /**
8306      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8307      */
8308     focusClass : "x-form-focus",  // not needed???
8309     
8310        
8311     /**
8312      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8313      */
8314     invalidClass : "has-warning",
8315     
8316     /**
8317      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8318      */
8319     validClass : "has-success",
8320     
8321     /**
8322      * @cfg {Boolean} hasFeedback (true|false) default true
8323      */
8324     hasFeedback : true,
8325     
8326     /**
8327      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8328      */
8329     invalidFeedbackClass : "glyphicon-warning-sign",
8330     
8331     /**
8332      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8333      */
8334     validFeedbackClass : "glyphicon-ok",
8335     
8336     /**
8337      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8338      */
8339     selectOnFocus : false,
8340     
8341      /**
8342      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8343      */
8344     maskRe : null,
8345        /**
8346      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8347      */
8348     vtype : null,
8349     
8350       /**
8351      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8352      */
8353     disableKeyFilter : false,
8354     
8355        /**
8356      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8357      */
8358     disabled : false,
8359      /**
8360      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8361      */
8362     allowBlank : true,
8363     /**
8364      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8365      */
8366     blankText : "Please complete this mandatory field",
8367     
8368      /**
8369      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8370      */
8371     minLength : 0,
8372     /**
8373      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8374      */
8375     maxLength : Number.MAX_VALUE,
8376     /**
8377      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8378      */
8379     minLengthText : "The minimum length for this field is {0}",
8380     /**
8381      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8382      */
8383     maxLengthText : "The maximum length for this field is {0}",
8384   
8385     
8386     /**
8387      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8388      * If available, this function will be called only after the basic validators all return true, and will be passed the
8389      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8390      */
8391     validator : null,
8392     /**
8393      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8394      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8395      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8396      */
8397     regex : null,
8398     /**
8399      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8400      */
8401     regexText : "",
8402     
8403     autocomplete: false,
8404     
8405     
8406     fieldLabel : '',
8407     inputType : 'text',
8408     
8409     name : false,
8410     placeholder: false,
8411     before : false,
8412     after : false,
8413     size : false,
8414     hasFocus : false,
8415     preventMark: false,
8416     isFormField : true,
8417     value : '',
8418     labelWidth : 2,
8419     labelAlign : false,
8420     readOnly : false,
8421     align : false,
8422     formatedValue : false,
8423     forceFeedback : false,
8424     
8425     indicatorpos : 'left',
8426     
8427     labellg : 0,
8428     labelmd : 0,
8429     labelsm : 0,
8430     labelxs : 0,
8431     
8432     parentLabelAlign : function()
8433     {
8434         var parent = this;
8435         while (parent.parent()) {
8436             parent = parent.parent();
8437             if (typeof(parent.labelAlign) !='undefined') {
8438                 return parent.labelAlign;
8439             }
8440         }
8441         return 'left';
8442         
8443     },
8444     
8445     getAutoCreate : function()
8446     {
8447         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8448         
8449         var id = Roo.id();
8450         
8451         var cfg = {};
8452         
8453         if(this.inputType != 'hidden'){
8454             cfg.cls = 'form-group' //input-group
8455         }
8456         
8457         var input =  {
8458             tag: 'input',
8459             id : id,
8460             type : this.inputType,
8461             value : this.value,
8462             cls : 'form-control',
8463             placeholder : this.placeholder || '',
8464             autocomplete : this.autocomplete || 'new-password'
8465         };
8466         
8467         if(this.align){
8468             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8469         }
8470         
8471         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8472             input.maxLength = this.maxLength;
8473         }
8474         
8475         if (this.disabled) {
8476             input.disabled=true;
8477         }
8478         
8479         if (this.readOnly) {
8480             input.readonly=true;
8481         }
8482         
8483         if (this.name) {
8484             input.name = this.name;
8485         }
8486         
8487         if (this.size) {
8488             input.cls += ' input-' + this.size;
8489         }
8490         
8491         var settings=this;
8492         ['xs','sm','md','lg'].map(function(size){
8493             if (settings[size]) {
8494                 cfg.cls += ' col-' + size + '-' + settings[size];
8495             }
8496         });
8497         
8498         var inputblock = input;
8499         
8500         var feedback = {
8501             tag: 'span',
8502             cls: 'glyphicon form-control-feedback'
8503         };
8504             
8505         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8506             
8507             inputblock = {
8508                 cls : 'has-feedback',
8509                 cn :  [
8510                     input,
8511                     feedback
8512                 ] 
8513             };  
8514         }
8515         
8516         if (this.before || this.after) {
8517             
8518             inputblock = {
8519                 cls : 'input-group',
8520                 cn :  [] 
8521             };
8522             
8523             if (this.before && typeof(this.before) == 'string') {
8524                 
8525                 inputblock.cn.push({
8526                     tag :'span',
8527                     cls : 'roo-input-before input-group-addon',
8528                     html : this.before
8529                 });
8530             }
8531             if (this.before && typeof(this.before) == 'object') {
8532                 this.before = Roo.factory(this.before);
8533                 
8534                 inputblock.cn.push({
8535                     tag :'span',
8536                     cls : 'roo-input-before input-group-' +
8537                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8538                 });
8539             }
8540             
8541             inputblock.cn.push(input);
8542             
8543             if (this.after && typeof(this.after) == 'string') {
8544                 inputblock.cn.push({
8545                     tag :'span',
8546                     cls : 'roo-input-after input-group-addon',
8547                     html : this.after
8548                 });
8549             }
8550             if (this.after && typeof(this.after) == 'object') {
8551                 this.after = Roo.factory(this.after);
8552                 
8553                 inputblock.cn.push({
8554                     tag :'span',
8555                     cls : 'roo-input-after input-group-' +
8556                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8557                 });
8558             }
8559             
8560             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8561                 inputblock.cls += ' has-feedback';
8562                 inputblock.cn.push(feedback);
8563             }
8564         };
8565         
8566         if (align ==='left' && this.fieldLabel.length) {
8567             
8568             cfg.cn = [
8569                 {
8570                     tag : 'i',
8571                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8572                     tooltip : 'This field is required'
8573                 },
8574                 {
8575                     tag: 'label',
8576                     'for' :  id,
8577                     cls : 'control-label',
8578                     html : this.fieldLabel
8579
8580                 },
8581                 {
8582                     cls : "", 
8583                     cn: [
8584                         inputblock
8585                     ]
8586                 }
8587             ];
8588             
8589             var labelCfg = cfg.cn[1];
8590             var contentCfg = cfg.cn[2];
8591             
8592             if(this.indicatorpos == 'right'){
8593                 cfg.cn = [
8594                     {
8595                         tag: 'label',
8596                         'for' :  id,
8597                         cls : 'control-label',
8598                         html : this.fieldLabel
8599
8600                     },
8601                     {
8602                         tag : 'i',
8603                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8604                         tooltip : 'This field is required'
8605                     },
8606                     {
8607                         cls : "",
8608                         cn: [
8609                             inputblock
8610                         ]
8611                     }
8612
8613                 ];
8614                 
8615                 labelCfg = cfg.cn[0];
8616             
8617             }
8618             
8619             if(this.labelWidth > 12){
8620                 labelCfg.style = "width: " + this.labelWidth + 'px';
8621             }
8622             
8623             if(this.labelWidth < 13 && this.labelmd == 0){
8624                 this.labelmd = this.labelWidth;
8625             }
8626             
8627             if(this.labellg > 0){
8628                 labelCfg.cls += ' col-lg-' + this.labellg;
8629                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8630             }
8631             
8632             if(this.labelmd > 0){
8633                 labelCfg.cls += ' col-md-' + this.labelmd;
8634                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8635             }
8636             
8637             if(this.labelsm > 0){
8638                 labelCfg.cls += ' col-sm-' + this.labelsm;
8639                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8640             }
8641             
8642             if(this.labelxs > 0){
8643                 labelCfg.cls += ' col-xs-' + this.labelxs;
8644                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8645             }
8646             
8647             
8648         } else if ( this.fieldLabel.length) {
8649                 
8650             cfg.cn = [
8651                 {
8652                     tag : 'i',
8653                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8654                     tooltip : 'This field is required'
8655                 },
8656                 {
8657                     tag: 'label',
8658                    //cls : 'input-group-addon',
8659                     html : this.fieldLabel
8660
8661                 },
8662
8663                inputblock
8664
8665            ];
8666            
8667            if(this.indicatorpos == 'right'){
8668                 
8669                 cfg.cn = [
8670                     {
8671                         tag: 'label',
8672                        //cls : 'input-group-addon',
8673                         html : this.fieldLabel
8674
8675                     },
8676                     {
8677                         tag : 'i',
8678                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8679                         tooltip : 'This field is required'
8680                     },
8681
8682                    inputblock
8683
8684                ];
8685
8686             }
8687
8688         } else {
8689             
8690             cfg.cn = [
8691
8692                     inputblock
8693
8694             ];
8695                 
8696                 
8697         };
8698         
8699         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8700            cfg.cls += ' navbar-form';
8701         }
8702         
8703         if (this.parentType === 'NavGroup') {
8704            cfg.cls += ' navbar-form';
8705            cfg.tag = 'li';
8706         }
8707         
8708         return cfg;
8709         
8710     },
8711     /**
8712      * return the real input element.
8713      */
8714     inputEl: function ()
8715     {
8716         return this.el.select('input.form-control',true).first();
8717     },
8718     
8719     tooltipEl : function()
8720     {
8721         return this.inputEl();
8722     },
8723     
8724     indicatorEl : function()
8725     {
8726         var indicator = this.el.select('i.roo-required-indicator',true).first();
8727         
8728         if(!indicator){
8729             return false;
8730         }
8731         
8732         return indicator;
8733         
8734     },
8735     
8736     setDisabled : function(v)
8737     {
8738         var i  = this.inputEl().dom;
8739         if (!v) {
8740             i.removeAttribute('disabled');
8741             return;
8742             
8743         }
8744         i.setAttribute('disabled','true');
8745     },
8746     initEvents : function()
8747     {
8748           
8749         this.inputEl().on("keydown" , this.fireKey,  this);
8750         this.inputEl().on("focus", this.onFocus,  this);
8751         this.inputEl().on("blur", this.onBlur,  this);
8752         
8753         this.inputEl().relayEvent('keyup', this);
8754         
8755         this.indicator = this.indicatorEl();
8756         
8757         if(this.indicator){
8758             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8759             this.indicator.hide();
8760         }
8761  
8762         // reference to original value for reset
8763         this.originalValue = this.getValue();
8764         //Roo.form.TextField.superclass.initEvents.call(this);
8765         if(this.validationEvent == 'keyup'){
8766             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8767             this.inputEl().on('keyup', this.filterValidation, this);
8768         }
8769         else if(this.validationEvent !== false){
8770             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8771         }
8772         
8773         if(this.selectOnFocus){
8774             this.on("focus", this.preFocus, this);
8775             
8776         }
8777         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8778             this.inputEl().on("keypress", this.filterKeys, this);
8779         } else {
8780             this.inputEl().relayEvent('keypress', this);
8781         }
8782        /* if(this.grow){
8783             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8784             this.el.on("click", this.autoSize,  this);
8785         }
8786         */
8787         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8788             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8789         }
8790         
8791         if (typeof(this.before) == 'object') {
8792             this.before.render(this.el.select('.roo-input-before',true).first());
8793         }
8794         if (typeof(this.after) == 'object') {
8795             this.after.render(this.el.select('.roo-input-after',true).first());
8796         }
8797         
8798         
8799     },
8800     filterValidation : function(e){
8801         if(!e.isNavKeyPress()){
8802             this.validationTask.delay(this.validationDelay);
8803         }
8804     },
8805      /**
8806      * Validates the field value
8807      * @return {Boolean} True if the value is valid, else false
8808      */
8809     validate : function(){
8810         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8811         if(this.disabled || this.validateValue(this.getRawValue())){
8812             this.markValid();
8813             return true;
8814         }
8815         
8816         this.markInvalid();
8817         return false;
8818     },
8819     
8820     
8821     /**
8822      * Validates a value according to the field's validation rules and marks the field as invalid
8823      * if the validation fails
8824      * @param {Mixed} value The value to validate
8825      * @return {Boolean} True if the value is valid, else false
8826      */
8827     validateValue : function(value){
8828         if(value.length < 1)  { // if it's blank
8829             if(this.allowBlank){
8830                 return true;
8831             }
8832             return false;
8833         }
8834         
8835         if(value.length < this.minLength){
8836             return false;
8837         }
8838         if(value.length > this.maxLength){
8839             return false;
8840         }
8841         if(this.vtype){
8842             var vt = Roo.form.VTypes;
8843             if(!vt[this.vtype](value, this)){
8844                 return false;
8845             }
8846         }
8847         if(typeof this.validator == "function"){
8848             var msg = this.validator(value);
8849             if(msg !== true){
8850                 return false;
8851             }
8852         }
8853         
8854         if(this.regex && !this.regex.test(value)){
8855             return false;
8856         }
8857         
8858         return true;
8859     },
8860
8861     
8862     
8863      // private
8864     fireKey : function(e){
8865         //Roo.log('field ' + e.getKey());
8866         if(e.isNavKeyPress()){
8867             this.fireEvent("specialkey", this, e);
8868         }
8869     },
8870     focus : function (selectText){
8871         if(this.rendered){
8872             this.inputEl().focus();
8873             if(selectText === true){
8874                 this.inputEl().dom.select();
8875             }
8876         }
8877         return this;
8878     } ,
8879     
8880     onFocus : function(){
8881         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8882            // this.el.addClass(this.focusClass);
8883         }
8884         if(!this.hasFocus){
8885             this.hasFocus = true;
8886             this.startValue = this.getValue();
8887             this.fireEvent("focus", this);
8888         }
8889     },
8890     
8891     beforeBlur : Roo.emptyFn,
8892
8893     
8894     // private
8895     onBlur : function(){
8896         this.beforeBlur();
8897         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8898             //this.el.removeClass(this.focusClass);
8899         }
8900         this.hasFocus = false;
8901         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8902             this.validate();
8903         }
8904         var v = this.getValue();
8905         if(String(v) !== String(this.startValue)){
8906             this.fireEvent('change', this, v, this.startValue);
8907         }
8908         this.fireEvent("blur", this);
8909     },
8910     
8911     /**
8912      * Resets the current field value to the originally loaded value and clears any validation messages
8913      */
8914     reset : function(){
8915         this.setValue(this.originalValue);
8916         this.validate();
8917     },
8918      /**
8919      * Returns the name of the field
8920      * @return {Mixed} name The name field
8921      */
8922     getName: function(){
8923         return this.name;
8924     },
8925      /**
8926      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8927      * @return {Mixed} value The field value
8928      */
8929     getValue : function(){
8930         
8931         var v = this.inputEl().getValue();
8932         
8933         return v;
8934     },
8935     /**
8936      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8937      * @return {Mixed} value The field value
8938      */
8939     getRawValue : function(){
8940         var v = this.inputEl().getValue();
8941         
8942         return v;
8943     },
8944     
8945     /**
8946      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8947      * @param {Mixed} value The value to set
8948      */
8949     setRawValue : function(v){
8950         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8951     },
8952     
8953     selectText : function(start, end){
8954         var v = this.getRawValue();
8955         if(v.length > 0){
8956             start = start === undefined ? 0 : start;
8957             end = end === undefined ? v.length : end;
8958             var d = this.inputEl().dom;
8959             if(d.setSelectionRange){
8960                 d.setSelectionRange(start, end);
8961             }else if(d.createTextRange){
8962                 var range = d.createTextRange();
8963                 range.moveStart("character", start);
8964                 range.moveEnd("character", v.length-end);
8965                 range.select();
8966             }
8967         }
8968     },
8969     
8970     /**
8971      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8972      * @param {Mixed} value The value to set
8973      */
8974     setValue : function(v){
8975         this.value = v;
8976         if(this.rendered){
8977             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8978             this.validate();
8979         }
8980     },
8981     
8982     /*
8983     processValue : function(value){
8984         if(this.stripCharsRe){
8985             var newValue = value.replace(this.stripCharsRe, '');
8986             if(newValue !== value){
8987                 this.setRawValue(newValue);
8988                 return newValue;
8989             }
8990         }
8991         return value;
8992     },
8993   */
8994     preFocus : function(){
8995         
8996         if(this.selectOnFocus){
8997             this.inputEl().dom.select();
8998         }
8999     },
9000     filterKeys : function(e){
9001         var k = e.getKey();
9002         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9003             return;
9004         }
9005         var c = e.getCharCode(), cc = String.fromCharCode(c);
9006         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9007             return;
9008         }
9009         if(!this.maskRe.test(cc)){
9010             e.stopEvent();
9011         }
9012     },
9013      /**
9014      * Clear any invalid styles/messages for this field
9015      */
9016     clearInvalid : function(){
9017         
9018         if(!this.el || this.preventMark){ // not rendered
9019             return;
9020         }
9021         
9022         if(this.indicator){
9023             this.indicator.hide();
9024         }
9025         
9026         this.el.removeClass(this.invalidClass);
9027         
9028         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9029             
9030             var feedback = this.el.select('.form-control-feedback', true).first();
9031             
9032             if(feedback){
9033                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9034             }
9035             
9036         }
9037         
9038         this.fireEvent('valid', this);
9039     },
9040     
9041      /**
9042      * Mark this field as valid
9043      */
9044     markValid : function()
9045     {
9046         if(!this.el  || this.preventMark){ // not rendered
9047             return;
9048         }
9049         
9050         this.el.removeClass([this.invalidClass, this.validClass]);
9051         
9052         var feedback = this.el.select('.form-control-feedback', true).first();
9053             
9054         if(feedback){
9055             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9056         }
9057
9058         if(this.disabled){
9059             return;
9060         }
9061         
9062         if(this.allowBlank && !this.getRawValue().length){
9063             return;
9064         }
9065         
9066         if(this.indicator){
9067             this.indicator.hide();
9068         }
9069         
9070         this.el.addClass(this.validClass);
9071         
9072         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
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, this.validFeedbackClass]);
9078                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9079             }
9080             
9081         }
9082         
9083         this.fireEvent('valid', this);
9084     },
9085     
9086      /**
9087      * Mark this field as invalid
9088      * @param {String} msg The validation message
9089      */
9090     markInvalid : function(msg)
9091     {
9092         if(!this.el  || this.preventMark){ // not rendered
9093             return;
9094         }
9095         
9096         this.el.removeClass([this.invalidClass, this.validClass]);
9097         
9098         var feedback = this.el.select('.form-control-feedback', true).first();
9099             
9100         if(feedback){
9101             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9102         }
9103
9104         if(this.disabled){
9105             return;
9106         }
9107         
9108         if(this.allowBlank && !this.getRawValue().length){
9109             return;
9110         }
9111         
9112         if(this.indicator){
9113             this.indicator.show();
9114         }
9115         
9116         this.el.addClass(this.invalidClass);
9117         
9118         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9119             
9120             var feedback = this.el.select('.form-control-feedback', true).first();
9121             
9122             if(feedback){
9123                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9124                 
9125                 if(this.getValue().length || this.forceFeedback){
9126                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9127                 }
9128                 
9129             }
9130             
9131         }
9132         
9133         this.fireEvent('invalid', this, msg);
9134     },
9135     // private
9136     SafariOnKeyDown : function(event)
9137     {
9138         // this is a workaround for a password hang bug on chrome/ webkit.
9139         if (this.inputEl().dom.type != 'password') {
9140             return;
9141         }
9142         
9143         var isSelectAll = false;
9144         
9145         if(this.inputEl().dom.selectionEnd > 0){
9146             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9147         }
9148         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9149             event.preventDefault();
9150             this.setValue('');
9151             return;
9152         }
9153         
9154         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9155             
9156             event.preventDefault();
9157             // this is very hacky as keydown always get's upper case.
9158             //
9159             var cc = String.fromCharCode(event.getCharCode());
9160             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9161             
9162         }
9163     },
9164     adjustWidth : function(tag, w){
9165         tag = tag.toLowerCase();
9166         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9167             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9168                 if(tag == 'input'){
9169                     return w + 2;
9170                 }
9171                 if(tag == 'textarea'){
9172                     return w-2;
9173                 }
9174             }else if(Roo.isOpera){
9175                 if(tag == 'input'){
9176                     return w + 2;
9177                 }
9178                 if(tag == 'textarea'){
9179                     return w-2;
9180                 }
9181             }
9182         }
9183         return w;
9184     }
9185     
9186 });
9187
9188  
9189 /*
9190  * - LGPL
9191  *
9192  * Input
9193  * 
9194  */
9195
9196 /**
9197  * @class Roo.bootstrap.TextArea
9198  * @extends Roo.bootstrap.Input
9199  * Bootstrap TextArea class
9200  * @cfg {Number} cols Specifies the visible width of a text area
9201  * @cfg {Number} rows Specifies the visible number of lines in a text area
9202  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9203  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9204  * @cfg {string} html text
9205  * 
9206  * @constructor
9207  * Create a new TextArea
9208  * @param {Object} config The config object
9209  */
9210
9211 Roo.bootstrap.TextArea = function(config){
9212     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9213    
9214 };
9215
9216 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9217      
9218     cols : false,
9219     rows : 5,
9220     readOnly : false,
9221     warp : 'soft',
9222     resize : false,
9223     value: false,
9224     html: false,
9225     
9226     getAutoCreate : function(){
9227         
9228         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9229         
9230         var id = Roo.id();
9231         
9232         var cfg = {};
9233         
9234         var input =  {
9235             tag: 'textarea',
9236             id : id,
9237             warp : this.warp,
9238             rows : this.rows,
9239             value : this.value || '',
9240             html: this.html || '',
9241             cls : 'form-control',
9242             placeholder : this.placeholder || '' 
9243             
9244         };
9245         
9246         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9247             input.maxLength = this.maxLength;
9248         }
9249         
9250         if(this.resize){
9251             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9252         }
9253         
9254         if(this.cols){
9255             input.cols = this.cols;
9256         }
9257         
9258         if (this.readOnly) {
9259             input.readonly = true;
9260         }
9261         
9262         if (this.name) {
9263             input.name = this.name;
9264         }
9265         
9266         if (this.size) {
9267             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9268         }
9269         
9270         var settings=this;
9271         ['xs','sm','md','lg'].map(function(size){
9272             if (settings[size]) {
9273                 cfg.cls += ' col-' + size + '-' + settings[size];
9274             }
9275         });
9276         
9277         var inputblock = input;
9278         
9279         if(this.hasFeedback && !this.allowBlank){
9280             
9281             var feedback = {
9282                 tag: 'span',
9283                 cls: 'glyphicon form-control-feedback'
9284             };
9285
9286             inputblock = {
9287                 cls : 'has-feedback',
9288                 cn :  [
9289                     input,
9290                     feedback
9291                 ] 
9292             };  
9293         }
9294         
9295         
9296         if (this.before || this.after) {
9297             
9298             inputblock = {
9299                 cls : 'input-group',
9300                 cn :  [] 
9301             };
9302             if (this.before) {
9303                 inputblock.cn.push({
9304                     tag :'span',
9305                     cls : 'input-group-addon',
9306                     html : this.before
9307                 });
9308             }
9309             
9310             inputblock.cn.push(input);
9311             
9312             if(this.hasFeedback && !this.allowBlank){
9313                 inputblock.cls += ' has-feedback';
9314                 inputblock.cn.push(feedback);
9315             }
9316             
9317             if (this.after) {
9318                 inputblock.cn.push({
9319                     tag :'span',
9320                     cls : 'input-group-addon',
9321                     html : this.after
9322                 });
9323             }
9324             
9325         }
9326         
9327         if (align ==='left' && this.fieldLabel.length) {
9328             cfg.cn = [
9329                 {
9330                     tag: 'label',
9331                     'for' :  id,
9332                     cls : 'control-label',
9333                     html : this.fieldLabel
9334                 },
9335                 {
9336                     cls : "",
9337                     cn: [
9338                         inputblock
9339                     ]
9340                 }
9341
9342             ];
9343             
9344             if(this.labelWidth > 12){
9345                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9346             }
9347
9348             if(this.labelWidth < 13 && this.labelmd == 0){
9349                 this.labelmd = this.labelWidth;
9350             }
9351
9352             if(this.labellg > 0){
9353                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9354                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9355             }
9356
9357             if(this.labelmd > 0){
9358                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9359                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9360             }
9361
9362             if(this.labelsm > 0){
9363                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9364                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9365             }
9366
9367             if(this.labelxs > 0){
9368                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9369                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9370             }
9371             
9372         } else if ( this.fieldLabel.length) {
9373             cfg.cn = [
9374
9375                {
9376                    tag: 'label',
9377                    //cls : 'input-group-addon',
9378                    html : this.fieldLabel
9379
9380                },
9381
9382                inputblock
9383
9384            ];
9385
9386         } else {
9387
9388             cfg.cn = [
9389
9390                 inputblock
9391
9392             ];
9393                 
9394         }
9395         
9396         if (this.disabled) {
9397             input.disabled=true;
9398         }
9399         
9400         return cfg;
9401         
9402     },
9403     /**
9404      * return the real textarea element.
9405      */
9406     inputEl: function ()
9407     {
9408         return this.el.select('textarea.form-control',true).first();
9409     },
9410     
9411     /**
9412      * Clear any invalid styles/messages for this field
9413      */
9414     clearInvalid : function()
9415     {
9416         
9417         if(!this.el || this.preventMark){ // not rendered
9418             return;
9419         }
9420         
9421         var label = this.el.select('label', true).first();
9422         var icon = this.el.select('i.fa-star', true).first();
9423         
9424         if(label && icon){
9425             icon.remove();
9426         }
9427         
9428         this.el.removeClass(this.invalidClass);
9429         
9430         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9431             
9432             var feedback = this.el.select('.form-control-feedback', true).first();
9433             
9434             if(feedback){
9435                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9436             }
9437             
9438         }
9439         
9440         this.fireEvent('valid', this);
9441     },
9442     
9443      /**
9444      * Mark this field as valid
9445      */
9446     markValid : function()
9447     {
9448         if(!this.el  || this.preventMark){ // not rendered
9449             return;
9450         }
9451         
9452         this.el.removeClass([this.invalidClass, this.validClass]);
9453         
9454         var feedback = this.el.select('.form-control-feedback', true).first();
9455             
9456         if(feedback){
9457             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9458         }
9459
9460         if(this.disabled || this.allowBlank){
9461             return;
9462         }
9463         
9464         var label = this.el.select('label', true).first();
9465         var icon = this.el.select('i.fa-star', true).first();
9466         
9467         if(label && icon){
9468             icon.remove();
9469         }
9470         
9471         this.el.addClass(this.validClass);
9472         
9473         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9474             
9475             var feedback = this.el.select('.form-control-feedback', true).first();
9476             
9477             if(feedback){
9478                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9479                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9480             }
9481             
9482         }
9483         
9484         this.fireEvent('valid', this);
9485     },
9486     
9487      /**
9488      * Mark this field as invalid
9489      * @param {String} msg The validation message
9490      */
9491     markInvalid : function(msg)
9492     {
9493         if(!this.el  || this.preventMark){ // not rendered
9494             return;
9495         }
9496         
9497         this.el.removeClass([this.invalidClass, this.validClass]);
9498         
9499         var feedback = this.el.select('.form-control-feedback', true).first();
9500             
9501         if(feedback){
9502             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9503         }
9504
9505         if(this.disabled || this.allowBlank){
9506             return;
9507         }
9508         
9509         var label = this.el.select('label', true).first();
9510         var icon = this.el.select('i.fa-star', true).first();
9511         
9512         if(!this.getValue().length && label && !icon){
9513             this.el.createChild({
9514                 tag : 'i',
9515                 cls : 'text-danger fa fa-lg fa-star',
9516                 tooltip : 'This field is required',
9517                 style : 'margin-right:5px;'
9518             }, label, true);
9519         }
9520
9521         this.el.addClass(this.invalidClass);
9522         
9523         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9524             
9525             var feedback = this.el.select('.form-control-feedback', true).first();
9526             
9527             if(feedback){
9528                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9529                 
9530                 if(this.getValue().length || this.forceFeedback){
9531                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9532                 }
9533                 
9534             }
9535             
9536         }
9537         
9538         this.fireEvent('invalid', this, msg);
9539     }
9540 });
9541
9542  
9543 /*
9544  * - LGPL
9545  *
9546  * trigger field - base class for combo..
9547  * 
9548  */
9549  
9550 /**
9551  * @class Roo.bootstrap.TriggerField
9552  * @extends Roo.bootstrap.Input
9553  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9554  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9555  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9556  * for which you can provide a custom implementation.  For example:
9557  * <pre><code>
9558 var trigger = new Roo.bootstrap.TriggerField();
9559 trigger.onTriggerClick = myTriggerFn;
9560 trigger.applyTo('my-field');
9561 </code></pre>
9562  *
9563  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9564  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9565  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9566  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9567  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9568
9569  * @constructor
9570  * Create a new TriggerField.
9571  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9572  * to the base TextField)
9573  */
9574 Roo.bootstrap.TriggerField = function(config){
9575     this.mimicing = false;
9576     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9577 };
9578
9579 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9580     /**
9581      * @cfg {String} triggerClass A CSS class to apply to the trigger
9582      */
9583      /**
9584      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9585      */
9586     hideTrigger:false,
9587
9588     /**
9589      * @cfg {Boolean} removable (true|false) special filter default false
9590      */
9591     removable : false,
9592     
9593     /** @cfg {Boolean} grow @hide */
9594     /** @cfg {Number} growMin @hide */
9595     /** @cfg {Number} growMax @hide */
9596
9597     /**
9598      * @hide 
9599      * @method
9600      */
9601     autoSize: Roo.emptyFn,
9602     // private
9603     monitorTab : true,
9604     // private
9605     deferHeight : true,
9606
9607     
9608     actionMode : 'wrap',
9609     
9610     caret : false,
9611     
9612     
9613     getAutoCreate : function(){
9614        
9615         var align = this.labelAlign || this.parentLabelAlign();
9616         
9617         var id = Roo.id();
9618         
9619         var cfg = {
9620             cls: 'form-group' //input-group
9621         };
9622         
9623         
9624         var input =  {
9625             tag: 'input',
9626             id : id,
9627             type : this.inputType,
9628             cls : 'form-control',
9629             autocomplete: 'new-password',
9630             placeholder : this.placeholder || '' 
9631             
9632         };
9633         if (this.name) {
9634             input.name = this.name;
9635         }
9636         if (this.size) {
9637             input.cls += ' input-' + this.size;
9638         }
9639         
9640         if (this.disabled) {
9641             input.disabled=true;
9642         }
9643         
9644         var inputblock = input;
9645         
9646         if(this.hasFeedback && !this.allowBlank){
9647             
9648             var feedback = {
9649                 tag: 'span',
9650                 cls: 'glyphicon form-control-feedback'
9651             };
9652             
9653             if(this.removable && !this.editable && !this.tickable){
9654                 inputblock = {
9655                     cls : 'has-feedback',
9656                     cn :  [
9657                         inputblock,
9658                         {
9659                             tag: 'button',
9660                             html : 'x',
9661                             cls : 'roo-combo-removable-btn close'
9662                         },
9663                         feedback
9664                     ] 
9665                 };
9666             } else {
9667                 inputblock = {
9668                     cls : 'has-feedback',
9669                     cn :  [
9670                         inputblock,
9671                         feedback
9672                     ] 
9673                 };
9674             }
9675
9676         } else {
9677             if(this.removable && !this.editable && !this.tickable){
9678                 inputblock = {
9679                     cls : 'roo-removable',
9680                     cn :  [
9681                         inputblock,
9682                         {
9683                             tag: 'button',
9684                             html : 'x',
9685                             cls : 'roo-combo-removable-btn close'
9686                         }
9687                     ] 
9688                 };
9689             }
9690         }
9691         
9692         if (this.before || this.after) {
9693             
9694             inputblock = {
9695                 cls : 'input-group',
9696                 cn :  [] 
9697             };
9698             if (this.before) {
9699                 inputblock.cn.push({
9700                     tag :'span',
9701                     cls : 'input-group-addon',
9702                     html : this.before
9703                 });
9704             }
9705             
9706             inputblock.cn.push(input);
9707             
9708             if(this.hasFeedback && !this.allowBlank){
9709                 inputblock.cls += ' has-feedback';
9710                 inputblock.cn.push(feedback);
9711             }
9712             
9713             if (this.after) {
9714                 inputblock.cn.push({
9715                     tag :'span',
9716                     cls : 'input-group-addon',
9717                     html : this.after
9718                 });
9719             }
9720             
9721         };
9722         
9723         var box = {
9724             tag: 'div',
9725             cn: [
9726                 {
9727                     tag: 'input',
9728                     type : 'hidden',
9729                     cls: 'form-hidden-field'
9730                 },
9731                 inputblock
9732             ]
9733             
9734         };
9735         
9736         if(this.multiple){
9737             box = {
9738                 tag: 'div',
9739                 cn: [
9740                     {
9741                         tag: 'input',
9742                         type : 'hidden',
9743                         cls: 'form-hidden-field'
9744                     },
9745                     {
9746                         tag: 'ul',
9747                         cls: 'roo-select2-choices',
9748                         cn:[
9749                             {
9750                                 tag: 'li',
9751                                 cls: 'roo-select2-search-field',
9752                                 cn: [
9753
9754                                     inputblock
9755                                 ]
9756                             }
9757                         ]
9758                     }
9759                 ]
9760             }
9761         };
9762         
9763         var combobox = {
9764             cls: 'roo-select2-container input-group',
9765             cn: [
9766                 box
9767 //                {
9768 //                    tag: 'ul',
9769 //                    cls: 'typeahead typeahead-long dropdown-menu',
9770 //                    style: 'display:none'
9771 //                }
9772             ]
9773         };
9774         
9775         if(!this.multiple && this.showToggleBtn){
9776             
9777             var caret = {
9778                         tag: 'span',
9779                         cls: 'caret'
9780              };
9781             if (this.caret != false) {
9782                 caret = {
9783                      tag: 'i',
9784                      cls: 'fa fa-' + this.caret
9785                 };
9786                 
9787             }
9788             
9789             combobox.cn.push({
9790                 tag :'span',
9791                 cls : 'input-group-addon btn dropdown-toggle',
9792                 cn : [
9793                     caret,
9794                     {
9795                         tag: 'span',
9796                         cls: 'combobox-clear',
9797                         cn  : [
9798                             {
9799                                 tag : 'i',
9800                                 cls: 'icon-remove'
9801                             }
9802                         ]
9803                     }
9804                 ]
9805
9806             })
9807         }
9808         
9809         if(this.multiple){
9810             combobox.cls += ' roo-select2-container-multi';
9811         }
9812         
9813         if (align ==='left' && this.fieldLabel.length) {
9814             
9815             cfg.cn = [
9816                 {
9817                     tag : 'i',
9818                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9819                     tooltip : 'This field is required'
9820                 },
9821                 {
9822                     tag: 'label',
9823                     'for' :  id,
9824                     cls : 'control-label',
9825                     html : this.fieldLabel
9826
9827                 },
9828                 {
9829                     cls : "", 
9830                     cn: [
9831                         combobox
9832                     ]
9833                 }
9834
9835             ];
9836             
9837             var labelCfg = cfg.cn[1];
9838             var contentCfg = cfg.cn[2];
9839             
9840             if(this.indicatorpos == 'right'){
9841                 cfg.cn = [
9842                     {
9843                         tag: 'label',
9844                         'for' :  id,
9845                         cls : 'control-label',
9846                         html : this.fieldLabel
9847
9848                     },
9849                     {
9850                         tag : 'i',
9851                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9852                         tooltip : 'This field is required'
9853                     },
9854                     {
9855                         cls : "", 
9856                         cn: [
9857                             combobox
9858                         ]
9859                     }
9860
9861                 ];
9862                 
9863                 labelCfg = cfg.cn[0];
9864             }
9865             
9866             if(this.labelWidth > 12){
9867                 labelCfg.style = "width: " + this.labelWidth + 'px';
9868             }
9869             
9870             if(this.labelWidth < 13 && this.labelmd == 0){
9871                 this.labelmd = this.labelWidth;
9872             }
9873             
9874             if(this.labellg > 0){
9875                 labelCfg.cls += ' col-lg-' + this.labellg;
9876                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9877             }
9878             
9879             if(this.labelmd > 0){
9880                 labelCfg.cls += ' col-md-' + this.labelmd;
9881                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9882             }
9883             
9884             if(this.labelsm > 0){
9885                 labelCfg.cls += ' col-sm-' + this.labelsm;
9886                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9887             }
9888             
9889             if(this.labelxs > 0){
9890                 labelCfg.cls += ' col-xs-' + this.labelxs;
9891                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9892             }
9893             
9894         } else if ( this.fieldLabel.length) {
9895 //                Roo.log(" label");
9896             cfg.cn = [
9897                 {
9898                    tag : 'i',
9899                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9900                    tooltip : 'This field is required'
9901                },
9902                {
9903                    tag: 'label',
9904                    //cls : 'input-group-addon',
9905                    html : this.fieldLabel
9906
9907                },
9908
9909                combobox
9910
9911             ];
9912             
9913             if(this.indicatorpos == 'right'){
9914                 
9915                 cfg.cn = [
9916                     {
9917                        tag: 'label',
9918                        //cls : 'input-group-addon',
9919                        html : this.fieldLabel
9920
9921                     },
9922                     {
9923                        tag : 'i',
9924                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9925                        tooltip : 'This field is required'
9926                     },
9927                     
9928                     combobox
9929
9930                 ];
9931
9932             }
9933
9934         } else {
9935             
9936 //                Roo.log(" no label && no align");
9937                 cfg = combobox
9938                      
9939                 
9940         }
9941          
9942         var settings=this;
9943         ['xs','sm','md','lg'].map(function(size){
9944             if (settings[size]) {
9945                 cfg.cls += ' col-' + size + '-' + settings[size];
9946             }
9947         });
9948         
9949         return cfg;
9950         
9951     },
9952     
9953     
9954     
9955     // private
9956     onResize : function(w, h){
9957 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9958 //        if(typeof w == 'number'){
9959 //            var x = w - this.trigger.getWidth();
9960 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9961 //            this.trigger.setStyle('left', x+'px');
9962 //        }
9963     },
9964
9965     // private
9966     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9967
9968     // private
9969     getResizeEl : function(){
9970         return this.inputEl();
9971     },
9972
9973     // private
9974     getPositionEl : function(){
9975         return this.inputEl();
9976     },
9977
9978     // private
9979     alignErrorIcon : function(){
9980         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9981     },
9982
9983     // private
9984     initEvents : function(){
9985         
9986         this.createList();
9987         
9988         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9989         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9990         if(!this.multiple && this.showToggleBtn){
9991             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9992             if(this.hideTrigger){
9993                 this.trigger.setDisplayed(false);
9994             }
9995             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9996         }
9997         
9998         if(this.multiple){
9999             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10000         }
10001         
10002         if(this.removable && !this.editable && !this.tickable){
10003             var close = this.closeTriggerEl();
10004             
10005             if(close){
10006                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10007                 close.on('click', this.removeBtnClick, this, close);
10008             }
10009         }
10010         
10011         //this.trigger.addClassOnOver('x-form-trigger-over');
10012         //this.trigger.addClassOnClick('x-form-trigger-click');
10013         
10014         //if(!this.width){
10015         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10016         //}
10017     },
10018     
10019     closeTriggerEl : function()
10020     {
10021         var close = this.el.select('.roo-combo-removable-btn', true).first();
10022         return close ? close : false;
10023     },
10024     
10025     removeBtnClick : function(e, h, el)
10026     {
10027         e.preventDefault();
10028         
10029         if(this.fireEvent("remove", this) !== false){
10030             this.reset();
10031             this.fireEvent("afterremove", this)
10032         }
10033     },
10034     
10035     createList : function()
10036     {
10037         this.list = Roo.get(document.body).createChild({
10038             tag: 'ul',
10039             cls: 'typeahead typeahead-long dropdown-menu',
10040             style: 'display:none'
10041         });
10042         
10043         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10044         
10045     },
10046
10047     // private
10048     initTrigger : function(){
10049        
10050     },
10051
10052     // private
10053     onDestroy : function(){
10054         if(this.trigger){
10055             this.trigger.removeAllListeners();
10056           //  this.trigger.remove();
10057         }
10058         //if(this.wrap){
10059         //    this.wrap.remove();
10060         //}
10061         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10062     },
10063
10064     // private
10065     onFocus : function(){
10066         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10067         /*
10068         if(!this.mimicing){
10069             this.wrap.addClass('x-trigger-wrap-focus');
10070             this.mimicing = true;
10071             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10072             if(this.monitorTab){
10073                 this.el.on("keydown", this.checkTab, this);
10074             }
10075         }
10076         */
10077     },
10078
10079     // private
10080     checkTab : function(e){
10081         if(e.getKey() == e.TAB){
10082             this.triggerBlur();
10083         }
10084     },
10085
10086     // private
10087     onBlur : function(){
10088         // do nothing
10089     },
10090
10091     // private
10092     mimicBlur : function(e, t){
10093         /*
10094         if(!this.wrap.contains(t) && this.validateBlur()){
10095             this.triggerBlur();
10096         }
10097         */
10098     },
10099
10100     // private
10101     triggerBlur : function(){
10102         this.mimicing = false;
10103         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10104         if(this.monitorTab){
10105             this.el.un("keydown", this.checkTab, this);
10106         }
10107         //this.wrap.removeClass('x-trigger-wrap-focus');
10108         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10109     },
10110
10111     // private
10112     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10113     validateBlur : function(e, t){
10114         return true;
10115     },
10116
10117     // private
10118     onDisable : function(){
10119         this.inputEl().dom.disabled = true;
10120         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10121         //if(this.wrap){
10122         //    this.wrap.addClass('x-item-disabled');
10123         //}
10124     },
10125
10126     // private
10127     onEnable : function(){
10128         this.inputEl().dom.disabled = false;
10129         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10130         //if(this.wrap){
10131         //    this.el.removeClass('x-item-disabled');
10132         //}
10133     },
10134
10135     // private
10136     onShow : function(){
10137         var ae = this.getActionEl();
10138         
10139         if(ae){
10140             ae.dom.style.display = '';
10141             ae.dom.style.visibility = 'visible';
10142         }
10143     },
10144
10145     // private
10146     
10147     onHide : function(){
10148         var ae = this.getActionEl();
10149         ae.dom.style.display = 'none';
10150     },
10151
10152     /**
10153      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10154      * by an implementing function.
10155      * @method
10156      * @param {EventObject} e
10157      */
10158     onTriggerClick : Roo.emptyFn
10159 });
10160  /*
10161  * Based on:
10162  * Ext JS Library 1.1.1
10163  * Copyright(c) 2006-2007, Ext JS, LLC.
10164  *
10165  * Originally Released Under LGPL - original licence link has changed is not relivant.
10166  *
10167  * Fork - LGPL
10168  * <script type="text/javascript">
10169  */
10170
10171
10172 /**
10173  * @class Roo.data.SortTypes
10174  * @singleton
10175  * Defines the default sorting (casting?) comparison functions used when sorting data.
10176  */
10177 Roo.data.SortTypes = {
10178     /**
10179      * Default sort that does nothing
10180      * @param {Mixed} s The value being converted
10181      * @return {Mixed} The comparison value
10182      */
10183     none : function(s){
10184         return s;
10185     },
10186     
10187     /**
10188      * The regular expression used to strip tags
10189      * @type {RegExp}
10190      * @property
10191      */
10192     stripTagsRE : /<\/?[^>]+>/gi,
10193     
10194     /**
10195      * Strips all HTML tags to sort on text only
10196      * @param {Mixed} s The value being converted
10197      * @return {String} The comparison value
10198      */
10199     asText : function(s){
10200         return String(s).replace(this.stripTagsRE, "");
10201     },
10202     
10203     /**
10204      * Strips all HTML tags to sort on text only - Case insensitive
10205      * @param {Mixed} s The value being converted
10206      * @return {String} The comparison value
10207      */
10208     asUCText : function(s){
10209         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10210     },
10211     
10212     /**
10213      * Case insensitive string
10214      * @param {Mixed} s The value being converted
10215      * @return {String} The comparison value
10216      */
10217     asUCString : function(s) {
10218         return String(s).toUpperCase();
10219     },
10220     
10221     /**
10222      * Date sorting
10223      * @param {Mixed} s The value being converted
10224      * @return {Number} The comparison value
10225      */
10226     asDate : function(s) {
10227         if(!s){
10228             return 0;
10229         }
10230         if(s instanceof Date){
10231             return s.getTime();
10232         }
10233         return Date.parse(String(s));
10234     },
10235     
10236     /**
10237      * Float sorting
10238      * @param {Mixed} s The value being converted
10239      * @return {Float} The comparison value
10240      */
10241     asFloat : function(s) {
10242         var val = parseFloat(String(s).replace(/,/g, ""));
10243         if(isNaN(val)) {
10244             val = 0;
10245         }
10246         return val;
10247     },
10248     
10249     /**
10250      * Integer sorting
10251      * @param {Mixed} s The value being converted
10252      * @return {Number} The comparison value
10253      */
10254     asInt : function(s) {
10255         var val = parseInt(String(s).replace(/,/g, ""));
10256         if(isNaN(val)) {
10257             val = 0;
10258         }
10259         return val;
10260     }
10261 };/*
10262  * Based on:
10263  * Ext JS Library 1.1.1
10264  * Copyright(c) 2006-2007, Ext JS, LLC.
10265  *
10266  * Originally Released Under LGPL - original licence link has changed is not relivant.
10267  *
10268  * Fork - LGPL
10269  * <script type="text/javascript">
10270  */
10271
10272 /**
10273 * @class Roo.data.Record
10274  * Instances of this class encapsulate both record <em>definition</em> information, and record
10275  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10276  * to access Records cached in an {@link Roo.data.Store} object.<br>
10277  * <p>
10278  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10279  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10280  * objects.<br>
10281  * <p>
10282  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10283  * @constructor
10284  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10285  * {@link #create}. The parameters are the same.
10286  * @param {Array} data An associative Array of data values keyed by the field name.
10287  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10288  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10289  * not specified an integer id is generated.
10290  */
10291 Roo.data.Record = function(data, id){
10292     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10293     this.data = data;
10294 };
10295
10296 /**
10297  * Generate a constructor for a specific record layout.
10298  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10299  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10300  * Each field definition object may contain the following properties: <ul>
10301  * <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,
10302  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10303  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10304  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10305  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10306  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10307  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10308  * this may be omitted.</p></li>
10309  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10310  * <ul><li>auto (Default, implies no conversion)</li>
10311  * <li>string</li>
10312  * <li>int</li>
10313  * <li>float</li>
10314  * <li>boolean</li>
10315  * <li>date</li></ul></p></li>
10316  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10317  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10318  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10319  * by the Reader into an object that will be stored in the Record. It is passed the
10320  * following parameters:<ul>
10321  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10322  * </ul></p></li>
10323  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10324  * </ul>
10325  * <br>usage:<br><pre><code>
10326 var TopicRecord = Roo.data.Record.create(
10327     {name: 'title', mapping: 'topic_title'},
10328     {name: 'author', mapping: 'username'},
10329     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10330     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10331     {name: 'lastPoster', mapping: 'user2'},
10332     {name: 'excerpt', mapping: 'post_text'}
10333 );
10334
10335 var myNewRecord = new TopicRecord({
10336     title: 'Do my job please',
10337     author: 'noobie',
10338     totalPosts: 1,
10339     lastPost: new Date(),
10340     lastPoster: 'Animal',
10341     excerpt: 'No way dude!'
10342 });
10343 myStore.add(myNewRecord);
10344 </code></pre>
10345  * @method create
10346  * @static
10347  */
10348 Roo.data.Record.create = function(o){
10349     var f = function(){
10350         f.superclass.constructor.apply(this, arguments);
10351     };
10352     Roo.extend(f, Roo.data.Record);
10353     var p = f.prototype;
10354     p.fields = new Roo.util.MixedCollection(false, function(field){
10355         return field.name;
10356     });
10357     for(var i = 0, len = o.length; i < len; i++){
10358         p.fields.add(new Roo.data.Field(o[i]));
10359     }
10360     f.getField = function(name){
10361         return p.fields.get(name);  
10362     };
10363     return f;
10364 };
10365
10366 Roo.data.Record.AUTO_ID = 1000;
10367 Roo.data.Record.EDIT = 'edit';
10368 Roo.data.Record.REJECT = 'reject';
10369 Roo.data.Record.COMMIT = 'commit';
10370
10371 Roo.data.Record.prototype = {
10372     /**
10373      * Readonly flag - true if this record has been modified.
10374      * @type Boolean
10375      */
10376     dirty : false,
10377     editing : false,
10378     error: null,
10379     modified: null,
10380
10381     // private
10382     join : function(store){
10383         this.store = store;
10384     },
10385
10386     /**
10387      * Set the named field to the specified value.
10388      * @param {String} name The name of the field to set.
10389      * @param {Object} value The value to set the field to.
10390      */
10391     set : function(name, value){
10392         if(this.data[name] == value){
10393             return;
10394         }
10395         this.dirty = true;
10396         if(!this.modified){
10397             this.modified = {};
10398         }
10399         if(typeof this.modified[name] == 'undefined'){
10400             this.modified[name] = this.data[name];
10401         }
10402         this.data[name] = value;
10403         if(!this.editing && this.store){
10404             this.store.afterEdit(this);
10405         }       
10406     },
10407
10408     /**
10409      * Get the value of the named field.
10410      * @param {String} name The name of the field to get the value of.
10411      * @return {Object} The value of the field.
10412      */
10413     get : function(name){
10414         return this.data[name]; 
10415     },
10416
10417     // private
10418     beginEdit : function(){
10419         this.editing = true;
10420         this.modified = {}; 
10421     },
10422
10423     // private
10424     cancelEdit : function(){
10425         this.editing = false;
10426         delete this.modified;
10427     },
10428
10429     // private
10430     endEdit : function(){
10431         this.editing = false;
10432         if(this.dirty && this.store){
10433             this.store.afterEdit(this);
10434         }
10435     },
10436
10437     /**
10438      * Usually called by the {@link Roo.data.Store} which owns the Record.
10439      * Rejects all changes made to the Record since either creation, or the last commit operation.
10440      * Modified fields are reverted to their original values.
10441      * <p>
10442      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10443      * of reject operations.
10444      */
10445     reject : function(){
10446         var m = this.modified;
10447         for(var n in m){
10448             if(typeof m[n] != "function"){
10449                 this.data[n] = m[n];
10450             }
10451         }
10452         this.dirty = false;
10453         delete this.modified;
10454         this.editing = false;
10455         if(this.store){
10456             this.store.afterReject(this);
10457         }
10458     },
10459
10460     /**
10461      * Usually called by the {@link Roo.data.Store} which owns the Record.
10462      * Commits all changes made to the Record since either creation, or the last commit operation.
10463      * <p>
10464      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10465      * of commit operations.
10466      */
10467     commit : function(){
10468         this.dirty = false;
10469         delete this.modified;
10470         this.editing = false;
10471         if(this.store){
10472             this.store.afterCommit(this);
10473         }
10474     },
10475
10476     // private
10477     hasError : function(){
10478         return this.error != null;
10479     },
10480
10481     // private
10482     clearError : function(){
10483         this.error = null;
10484     },
10485
10486     /**
10487      * Creates a copy of this record.
10488      * @param {String} id (optional) A new record id if you don't want to use this record's id
10489      * @return {Record}
10490      */
10491     copy : function(newId) {
10492         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10493     }
10494 };/*
10495  * Based on:
10496  * Ext JS Library 1.1.1
10497  * Copyright(c) 2006-2007, Ext JS, LLC.
10498  *
10499  * Originally Released Under LGPL - original licence link has changed is not relivant.
10500  *
10501  * Fork - LGPL
10502  * <script type="text/javascript">
10503  */
10504
10505
10506
10507 /**
10508  * @class Roo.data.Store
10509  * @extends Roo.util.Observable
10510  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10511  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10512  * <p>
10513  * 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
10514  * has no knowledge of the format of the data returned by the Proxy.<br>
10515  * <p>
10516  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10517  * instances from the data object. These records are cached and made available through accessor functions.
10518  * @constructor
10519  * Creates a new Store.
10520  * @param {Object} config A config object containing the objects needed for the Store to access data,
10521  * and read the data into Records.
10522  */
10523 Roo.data.Store = function(config){
10524     this.data = new Roo.util.MixedCollection(false);
10525     this.data.getKey = function(o){
10526         return o.id;
10527     };
10528     this.baseParams = {};
10529     // private
10530     this.paramNames = {
10531         "start" : "start",
10532         "limit" : "limit",
10533         "sort" : "sort",
10534         "dir" : "dir",
10535         "multisort" : "_multisort"
10536     };
10537
10538     if(config && config.data){
10539         this.inlineData = config.data;
10540         delete config.data;
10541     }
10542
10543     Roo.apply(this, config);
10544     
10545     if(this.reader){ // reader passed
10546         this.reader = Roo.factory(this.reader, Roo.data);
10547         this.reader.xmodule = this.xmodule || false;
10548         if(!this.recordType){
10549             this.recordType = this.reader.recordType;
10550         }
10551         if(this.reader.onMetaChange){
10552             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10553         }
10554     }
10555
10556     if(this.recordType){
10557         this.fields = this.recordType.prototype.fields;
10558     }
10559     this.modified = [];
10560
10561     this.addEvents({
10562         /**
10563          * @event datachanged
10564          * Fires when the data cache has changed, and a widget which is using this Store
10565          * as a Record cache should refresh its view.
10566          * @param {Store} this
10567          */
10568         datachanged : true,
10569         /**
10570          * @event metachange
10571          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10572          * @param {Store} this
10573          * @param {Object} meta The JSON metadata
10574          */
10575         metachange : true,
10576         /**
10577          * @event add
10578          * Fires when Records have been added to the Store
10579          * @param {Store} this
10580          * @param {Roo.data.Record[]} records The array of Records added
10581          * @param {Number} index The index at which the record(s) were added
10582          */
10583         add : true,
10584         /**
10585          * @event remove
10586          * Fires when a Record has been removed from the Store
10587          * @param {Store} this
10588          * @param {Roo.data.Record} record The Record that was removed
10589          * @param {Number} index The index at which the record was removed
10590          */
10591         remove : true,
10592         /**
10593          * @event update
10594          * Fires when a Record has been updated
10595          * @param {Store} this
10596          * @param {Roo.data.Record} record The Record that was updated
10597          * @param {String} operation The update operation being performed.  Value may be one of:
10598          * <pre><code>
10599  Roo.data.Record.EDIT
10600  Roo.data.Record.REJECT
10601  Roo.data.Record.COMMIT
10602          * </code></pre>
10603          */
10604         update : true,
10605         /**
10606          * @event clear
10607          * Fires when the data cache has been cleared.
10608          * @param {Store} this
10609          */
10610         clear : true,
10611         /**
10612          * @event beforeload
10613          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10614          * the load action will be canceled.
10615          * @param {Store} this
10616          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10617          */
10618         beforeload : true,
10619         /**
10620          * @event beforeloadadd
10621          * Fires after a new set of Records has been loaded.
10622          * @param {Store} this
10623          * @param {Roo.data.Record[]} records The Records that were loaded
10624          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10625          */
10626         beforeloadadd : true,
10627         /**
10628          * @event load
10629          * Fires after a new set of Records has been loaded, before they are added to the store.
10630          * @param {Store} this
10631          * @param {Roo.data.Record[]} records The Records that were loaded
10632          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10633          * @params {Object} return from reader
10634          */
10635         load : true,
10636         /**
10637          * @event loadexception
10638          * Fires if an exception occurs in the Proxy during loading.
10639          * Called with the signature of the Proxy's "loadexception" event.
10640          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10641          * 
10642          * @param {Proxy} 
10643          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10644          * @param {Object} load options 
10645          * @param {Object} jsonData from your request (normally this contains the Exception)
10646          */
10647         loadexception : true
10648     });
10649     
10650     if(this.proxy){
10651         this.proxy = Roo.factory(this.proxy, Roo.data);
10652         this.proxy.xmodule = this.xmodule || false;
10653         this.relayEvents(this.proxy,  ["loadexception"]);
10654     }
10655     this.sortToggle = {};
10656     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10657
10658     Roo.data.Store.superclass.constructor.call(this);
10659
10660     if(this.inlineData){
10661         this.loadData(this.inlineData);
10662         delete this.inlineData;
10663     }
10664 };
10665
10666 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10667      /**
10668     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10669     * without a remote query - used by combo/forms at present.
10670     */
10671     
10672     /**
10673     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10674     */
10675     /**
10676     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10677     */
10678     /**
10679     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10680     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10681     */
10682     /**
10683     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10684     * on any HTTP request
10685     */
10686     /**
10687     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10688     */
10689     /**
10690     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10691     */
10692     multiSort: false,
10693     /**
10694     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10695     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10696     */
10697     remoteSort : false,
10698
10699     /**
10700     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10701      * loaded or when a record is removed. (defaults to false).
10702     */
10703     pruneModifiedRecords : false,
10704
10705     // private
10706     lastOptions : null,
10707
10708     /**
10709      * Add Records to the Store and fires the add event.
10710      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10711      */
10712     add : function(records){
10713         records = [].concat(records);
10714         for(var i = 0, len = records.length; i < len; i++){
10715             records[i].join(this);
10716         }
10717         var index = this.data.length;
10718         this.data.addAll(records);
10719         this.fireEvent("add", this, records, index);
10720     },
10721
10722     /**
10723      * Remove a Record from the Store and fires the remove event.
10724      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10725      */
10726     remove : function(record){
10727         var index = this.data.indexOf(record);
10728         this.data.removeAt(index);
10729         if(this.pruneModifiedRecords){
10730             this.modified.remove(record);
10731         }
10732         this.fireEvent("remove", this, record, index);
10733     },
10734
10735     /**
10736      * Remove all Records from the Store and fires the clear event.
10737      */
10738     removeAll : function(){
10739         this.data.clear();
10740         if(this.pruneModifiedRecords){
10741             this.modified = [];
10742         }
10743         this.fireEvent("clear", this);
10744     },
10745
10746     /**
10747      * Inserts Records to the Store at the given index and fires the add event.
10748      * @param {Number} index The start index at which to insert the passed Records.
10749      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10750      */
10751     insert : function(index, records){
10752         records = [].concat(records);
10753         for(var i = 0, len = records.length; i < len; i++){
10754             this.data.insert(index, records[i]);
10755             records[i].join(this);
10756         }
10757         this.fireEvent("add", this, records, index);
10758     },
10759
10760     /**
10761      * Get the index within the cache of the passed Record.
10762      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10763      * @return {Number} The index of the passed Record. Returns -1 if not found.
10764      */
10765     indexOf : function(record){
10766         return this.data.indexOf(record);
10767     },
10768
10769     /**
10770      * Get the index within the cache of the Record with the passed id.
10771      * @param {String} id The id of the Record to find.
10772      * @return {Number} The index of the Record. Returns -1 if not found.
10773      */
10774     indexOfId : function(id){
10775         return this.data.indexOfKey(id);
10776     },
10777
10778     /**
10779      * Get the Record with the specified id.
10780      * @param {String} id The id of the Record to find.
10781      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10782      */
10783     getById : function(id){
10784         return this.data.key(id);
10785     },
10786
10787     /**
10788      * Get the Record at the specified index.
10789      * @param {Number} index The index of the Record to find.
10790      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10791      */
10792     getAt : function(index){
10793         return this.data.itemAt(index);
10794     },
10795
10796     /**
10797      * Returns a range of Records between specified indices.
10798      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10799      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10800      * @return {Roo.data.Record[]} An array of Records
10801      */
10802     getRange : function(start, end){
10803         return this.data.getRange(start, end);
10804     },
10805
10806     // private
10807     storeOptions : function(o){
10808         o = Roo.apply({}, o);
10809         delete o.callback;
10810         delete o.scope;
10811         this.lastOptions = o;
10812     },
10813
10814     /**
10815      * Loads the Record cache from the configured Proxy using the configured Reader.
10816      * <p>
10817      * If using remote paging, then the first load call must specify the <em>start</em>
10818      * and <em>limit</em> properties in the options.params property to establish the initial
10819      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10820      * <p>
10821      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10822      * and this call will return before the new data has been loaded. Perform any post-processing
10823      * in a callback function, or in a "load" event handler.</strong>
10824      * <p>
10825      * @param {Object} options An object containing properties which control loading options:<ul>
10826      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10827      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10828      * passed the following arguments:<ul>
10829      * <li>r : Roo.data.Record[]</li>
10830      * <li>options: Options object from the load call</li>
10831      * <li>success: Boolean success indicator</li></ul></li>
10832      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10833      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10834      * </ul>
10835      */
10836     load : function(options){
10837         options = options || {};
10838         if(this.fireEvent("beforeload", this, options) !== false){
10839             this.storeOptions(options);
10840             var p = Roo.apply(options.params || {}, this.baseParams);
10841             // if meta was not loaded from remote source.. try requesting it.
10842             if (!this.reader.metaFromRemote) {
10843                 p._requestMeta = 1;
10844             }
10845             if(this.sortInfo && this.remoteSort){
10846                 var pn = this.paramNames;
10847                 p[pn["sort"]] = this.sortInfo.field;
10848                 p[pn["dir"]] = this.sortInfo.direction;
10849             }
10850             if (this.multiSort) {
10851                 var pn = this.paramNames;
10852                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10853             }
10854             
10855             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10856         }
10857     },
10858
10859     /**
10860      * Reloads the Record cache from the configured Proxy using the configured Reader and
10861      * the options from the last load operation performed.
10862      * @param {Object} options (optional) An object containing properties which may override the options
10863      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10864      * the most recently used options are reused).
10865      */
10866     reload : function(options){
10867         this.load(Roo.applyIf(options||{}, this.lastOptions));
10868     },
10869
10870     // private
10871     // Called as a callback by the Reader during a load operation.
10872     loadRecords : function(o, options, success){
10873         if(!o || success === false){
10874             if(success !== false){
10875                 this.fireEvent("load", this, [], options, o);
10876             }
10877             if(options.callback){
10878                 options.callback.call(options.scope || this, [], options, false);
10879             }
10880             return;
10881         }
10882         // if data returned failure - throw an exception.
10883         if (o.success === false) {
10884             // show a message if no listener is registered.
10885             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10886                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10887             }
10888             // loadmask wil be hooked into this..
10889             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10890             return;
10891         }
10892         var r = o.records, t = o.totalRecords || r.length;
10893         
10894         this.fireEvent("beforeloadadd", this, r, options, o);
10895         
10896         if(!options || options.add !== true){
10897             if(this.pruneModifiedRecords){
10898                 this.modified = [];
10899             }
10900             for(var i = 0, len = r.length; i < len; i++){
10901                 r[i].join(this);
10902             }
10903             if(this.snapshot){
10904                 this.data = this.snapshot;
10905                 delete this.snapshot;
10906             }
10907             this.data.clear();
10908             this.data.addAll(r);
10909             this.totalLength = t;
10910             this.applySort();
10911             this.fireEvent("datachanged", this);
10912         }else{
10913             this.totalLength = Math.max(t, this.data.length+r.length);
10914             this.add(r);
10915         }
10916         this.fireEvent("load", this, r, options, o);
10917         if(options.callback){
10918             options.callback.call(options.scope || this, r, options, true);
10919         }
10920     },
10921
10922
10923     /**
10924      * Loads data from a passed data block. A Reader which understands the format of the data
10925      * must have been configured in the constructor.
10926      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10927      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10928      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10929      */
10930     loadData : function(o, append){
10931         var r = this.reader.readRecords(o);
10932         this.loadRecords(r, {add: append}, true);
10933     },
10934
10935     /**
10936      * Gets the number of cached records.
10937      * <p>
10938      * <em>If using paging, this may not be the total size of the dataset. If the data object
10939      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10940      * the data set size</em>
10941      */
10942     getCount : function(){
10943         return this.data.length || 0;
10944     },
10945
10946     /**
10947      * Gets the total number of records in the dataset as returned by the server.
10948      * <p>
10949      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10950      * the dataset size</em>
10951      */
10952     getTotalCount : function(){
10953         return this.totalLength || 0;
10954     },
10955
10956     /**
10957      * Returns the sort state of the Store as an object with two properties:
10958      * <pre><code>
10959  field {String} The name of the field by which the Records are sorted
10960  direction {String} The sort order, "ASC" or "DESC"
10961      * </code></pre>
10962      */
10963     getSortState : function(){
10964         return this.sortInfo;
10965     },
10966
10967     // private
10968     applySort : function(){
10969         if(this.sortInfo && !this.remoteSort){
10970             var s = this.sortInfo, f = s.field;
10971             var st = this.fields.get(f).sortType;
10972             var fn = function(r1, r2){
10973                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10974                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10975             };
10976             this.data.sort(s.direction, fn);
10977             if(this.snapshot && this.snapshot != this.data){
10978                 this.snapshot.sort(s.direction, fn);
10979             }
10980         }
10981     },
10982
10983     /**
10984      * Sets the default sort column and order to be used by the next load operation.
10985      * @param {String} fieldName The name of the field to sort by.
10986      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10987      */
10988     setDefaultSort : function(field, dir){
10989         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10990     },
10991
10992     /**
10993      * Sort the Records.
10994      * If remote sorting is used, the sort is performed on the server, and the cache is
10995      * reloaded. If local sorting is used, the cache is sorted internally.
10996      * @param {String} fieldName The name of the field to sort by.
10997      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10998      */
10999     sort : function(fieldName, dir){
11000         var f = this.fields.get(fieldName);
11001         if(!dir){
11002             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11003             
11004             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11005                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11006             }else{
11007                 dir = f.sortDir;
11008             }
11009         }
11010         this.sortToggle[f.name] = dir;
11011         this.sortInfo = {field: f.name, direction: dir};
11012         if(!this.remoteSort){
11013             this.applySort();
11014             this.fireEvent("datachanged", this);
11015         }else{
11016             this.load(this.lastOptions);
11017         }
11018     },
11019
11020     /**
11021      * Calls the specified function for each of the Records in the cache.
11022      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11023      * Returning <em>false</em> aborts and exits the iteration.
11024      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11025      */
11026     each : function(fn, scope){
11027         this.data.each(fn, scope);
11028     },
11029
11030     /**
11031      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11032      * (e.g., during paging).
11033      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11034      */
11035     getModifiedRecords : function(){
11036         return this.modified;
11037     },
11038
11039     // private
11040     createFilterFn : function(property, value, anyMatch){
11041         if(!value.exec){ // not a regex
11042             value = String(value);
11043             if(value.length == 0){
11044                 return false;
11045             }
11046             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11047         }
11048         return function(r){
11049             return value.test(r.data[property]);
11050         };
11051     },
11052
11053     /**
11054      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11055      * @param {String} property A field on your records
11056      * @param {Number} start The record index to start at (defaults to 0)
11057      * @param {Number} end The last record index to include (defaults to length - 1)
11058      * @return {Number} The sum
11059      */
11060     sum : function(property, start, end){
11061         var rs = this.data.items, v = 0;
11062         start = start || 0;
11063         end = (end || end === 0) ? end : rs.length-1;
11064
11065         for(var i = start; i <= end; i++){
11066             v += (rs[i].data[property] || 0);
11067         }
11068         return v;
11069     },
11070
11071     /**
11072      * Filter the records by a specified property.
11073      * @param {String} field A field on your records
11074      * @param {String/RegExp} value Either a string that the field
11075      * should start with or a RegExp to test against the field
11076      * @param {Boolean} anyMatch True to match any part not just the beginning
11077      */
11078     filter : function(property, value, anyMatch){
11079         var fn = this.createFilterFn(property, value, anyMatch);
11080         return fn ? this.filterBy(fn) : this.clearFilter();
11081     },
11082
11083     /**
11084      * Filter by a function. The specified function will be called with each
11085      * record in this data source. If the function returns true the record is included,
11086      * otherwise it is filtered.
11087      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11088      * @param {Object} scope (optional) The scope of the function (defaults to this)
11089      */
11090     filterBy : function(fn, scope){
11091         this.snapshot = this.snapshot || this.data;
11092         this.data = this.queryBy(fn, scope||this);
11093         this.fireEvent("datachanged", this);
11094     },
11095
11096     /**
11097      * Query the records by a specified property.
11098      * @param {String} field A field on your records
11099      * @param {String/RegExp} value Either a string that the field
11100      * should start with or a RegExp to test against the field
11101      * @param {Boolean} anyMatch True to match any part not just the beginning
11102      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11103      */
11104     query : function(property, value, anyMatch){
11105         var fn = this.createFilterFn(property, value, anyMatch);
11106         return fn ? this.queryBy(fn) : this.data.clone();
11107     },
11108
11109     /**
11110      * Query by a function. The specified function will be called with each
11111      * record in this data source. If the function returns true the record is included
11112      * in the results.
11113      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11114      * @param {Object} scope (optional) The scope of the function (defaults to this)
11115       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11116      **/
11117     queryBy : function(fn, scope){
11118         var data = this.snapshot || this.data;
11119         return data.filterBy(fn, scope||this);
11120     },
11121
11122     /**
11123      * Collects unique values for a particular dataIndex from this store.
11124      * @param {String} dataIndex The property to collect
11125      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11126      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11127      * @return {Array} An array of the unique values
11128      **/
11129     collect : function(dataIndex, allowNull, bypassFilter){
11130         var d = (bypassFilter === true && this.snapshot) ?
11131                 this.snapshot.items : this.data.items;
11132         var v, sv, r = [], l = {};
11133         for(var i = 0, len = d.length; i < len; i++){
11134             v = d[i].data[dataIndex];
11135             sv = String(v);
11136             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11137                 l[sv] = true;
11138                 r[r.length] = v;
11139             }
11140         }
11141         return r;
11142     },
11143
11144     /**
11145      * Revert to a view of the Record cache with no filtering applied.
11146      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11147      */
11148     clearFilter : function(suppressEvent){
11149         if(this.snapshot && this.snapshot != this.data){
11150             this.data = this.snapshot;
11151             delete this.snapshot;
11152             if(suppressEvent !== true){
11153                 this.fireEvent("datachanged", this);
11154             }
11155         }
11156     },
11157
11158     // private
11159     afterEdit : function(record){
11160         if(this.modified.indexOf(record) == -1){
11161             this.modified.push(record);
11162         }
11163         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11164     },
11165     
11166     // private
11167     afterReject : function(record){
11168         this.modified.remove(record);
11169         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11170     },
11171
11172     // private
11173     afterCommit : function(record){
11174         this.modified.remove(record);
11175         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11176     },
11177
11178     /**
11179      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11180      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11181      */
11182     commitChanges : function(){
11183         var m = this.modified.slice(0);
11184         this.modified = [];
11185         for(var i = 0, len = m.length; i < len; i++){
11186             m[i].commit();
11187         }
11188     },
11189
11190     /**
11191      * Cancel outstanding changes on all changed records.
11192      */
11193     rejectChanges : function(){
11194         var m = this.modified.slice(0);
11195         this.modified = [];
11196         for(var i = 0, len = m.length; i < len; i++){
11197             m[i].reject();
11198         }
11199     },
11200
11201     onMetaChange : function(meta, rtype, o){
11202         this.recordType = rtype;
11203         this.fields = rtype.prototype.fields;
11204         delete this.snapshot;
11205         this.sortInfo = meta.sortInfo || this.sortInfo;
11206         this.modified = [];
11207         this.fireEvent('metachange', this, this.reader.meta);
11208     },
11209     
11210     moveIndex : function(data, type)
11211     {
11212         var index = this.indexOf(data);
11213         
11214         var newIndex = index + type;
11215         
11216         this.remove(data);
11217         
11218         this.insert(newIndex, data);
11219         
11220     }
11221 });/*
11222  * Based on:
11223  * Ext JS Library 1.1.1
11224  * Copyright(c) 2006-2007, Ext JS, LLC.
11225  *
11226  * Originally Released Under LGPL - original licence link has changed is not relivant.
11227  *
11228  * Fork - LGPL
11229  * <script type="text/javascript">
11230  */
11231
11232 /**
11233  * @class Roo.data.SimpleStore
11234  * @extends Roo.data.Store
11235  * Small helper class to make creating Stores from Array data easier.
11236  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11237  * @cfg {Array} fields An array of field definition objects, or field name strings.
11238  * @cfg {Array} data The multi-dimensional array of data
11239  * @constructor
11240  * @param {Object} config
11241  */
11242 Roo.data.SimpleStore = function(config){
11243     Roo.data.SimpleStore.superclass.constructor.call(this, {
11244         isLocal : true,
11245         reader: new Roo.data.ArrayReader({
11246                 id: config.id
11247             },
11248             Roo.data.Record.create(config.fields)
11249         ),
11250         proxy : new Roo.data.MemoryProxy(config.data)
11251     });
11252     this.load();
11253 };
11254 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11255  * Based on:
11256  * Ext JS Library 1.1.1
11257  * Copyright(c) 2006-2007, Ext JS, LLC.
11258  *
11259  * Originally Released Under LGPL - original licence link has changed is not relivant.
11260  *
11261  * Fork - LGPL
11262  * <script type="text/javascript">
11263  */
11264
11265 /**
11266 /**
11267  * @extends Roo.data.Store
11268  * @class Roo.data.JsonStore
11269  * Small helper class to make creating Stores for JSON data easier. <br/>
11270 <pre><code>
11271 var store = new Roo.data.JsonStore({
11272     url: 'get-images.php',
11273     root: 'images',
11274     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11275 });
11276 </code></pre>
11277  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11278  * JsonReader and HttpProxy (unless inline data is provided).</b>
11279  * @cfg {Array} fields An array of field definition objects, or field name strings.
11280  * @constructor
11281  * @param {Object} config
11282  */
11283 Roo.data.JsonStore = function(c){
11284     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11285         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11286         reader: new Roo.data.JsonReader(c, c.fields)
11287     }));
11288 };
11289 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11290  * Based on:
11291  * Ext JS Library 1.1.1
11292  * Copyright(c) 2006-2007, Ext JS, LLC.
11293  *
11294  * Originally Released Under LGPL - original licence link has changed is not relivant.
11295  *
11296  * Fork - LGPL
11297  * <script type="text/javascript">
11298  */
11299
11300  
11301 Roo.data.Field = function(config){
11302     if(typeof config == "string"){
11303         config = {name: config};
11304     }
11305     Roo.apply(this, config);
11306     
11307     if(!this.type){
11308         this.type = "auto";
11309     }
11310     
11311     var st = Roo.data.SortTypes;
11312     // named sortTypes are supported, here we look them up
11313     if(typeof this.sortType == "string"){
11314         this.sortType = st[this.sortType];
11315     }
11316     
11317     // set default sortType for strings and dates
11318     if(!this.sortType){
11319         switch(this.type){
11320             case "string":
11321                 this.sortType = st.asUCString;
11322                 break;
11323             case "date":
11324                 this.sortType = st.asDate;
11325                 break;
11326             default:
11327                 this.sortType = st.none;
11328         }
11329     }
11330
11331     // define once
11332     var stripRe = /[\$,%]/g;
11333
11334     // prebuilt conversion function for this field, instead of
11335     // switching every time we're reading a value
11336     if(!this.convert){
11337         var cv, dateFormat = this.dateFormat;
11338         switch(this.type){
11339             case "":
11340             case "auto":
11341             case undefined:
11342                 cv = function(v){ return v; };
11343                 break;
11344             case "string":
11345                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11346                 break;
11347             case "int":
11348                 cv = function(v){
11349                     return v !== undefined && v !== null && v !== '' ?
11350                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11351                     };
11352                 break;
11353             case "float":
11354                 cv = function(v){
11355                     return v !== undefined && v !== null && v !== '' ?
11356                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11357                     };
11358                 break;
11359             case "bool":
11360             case "boolean":
11361                 cv = function(v){ return v === true || v === "true" || v == 1; };
11362                 break;
11363             case "date":
11364                 cv = function(v){
11365                     if(!v){
11366                         return '';
11367                     }
11368                     if(v instanceof Date){
11369                         return v;
11370                     }
11371                     if(dateFormat){
11372                         if(dateFormat == "timestamp"){
11373                             return new Date(v*1000);
11374                         }
11375                         return Date.parseDate(v, dateFormat);
11376                     }
11377                     var parsed = Date.parse(v);
11378                     return parsed ? new Date(parsed) : null;
11379                 };
11380              break;
11381             
11382         }
11383         this.convert = cv;
11384     }
11385 };
11386
11387 Roo.data.Field.prototype = {
11388     dateFormat: null,
11389     defaultValue: "",
11390     mapping: null,
11391     sortType : null,
11392     sortDir : "ASC"
11393 };/*
11394  * Based on:
11395  * Ext JS Library 1.1.1
11396  * Copyright(c) 2006-2007, Ext JS, LLC.
11397  *
11398  * Originally Released Under LGPL - original licence link has changed is not relivant.
11399  *
11400  * Fork - LGPL
11401  * <script type="text/javascript">
11402  */
11403  
11404 // Base class for reading structured data from a data source.  This class is intended to be
11405 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11406
11407 /**
11408  * @class Roo.data.DataReader
11409  * Base class for reading structured data from a data source.  This class is intended to be
11410  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11411  */
11412
11413 Roo.data.DataReader = function(meta, recordType){
11414     
11415     this.meta = meta;
11416     
11417     this.recordType = recordType instanceof Array ? 
11418         Roo.data.Record.create(recordType) : recordType;
11419 };
11420
11421 Roo.data.DataReader.prototype = {
11422      /**
11423      * Create an empty record
11424      * @param {Object} data (optional) - overlay some values
11425      * @return {Roo.data.Record} record created.
11426      */
11427     newRow :  function(d) {
11428         var da =  {};
11429         this.recordType.prototype.fields.each(function(c) {
11430             switch( c.type) {
11431                 case 'int' : da[c.name] = 0; break;
11432                 case 'date' : da[c.name] = new Date(); break;
11433                 case 'float' : da[c.name] = 0.0; break;
11434                 case 'boolean' : da[c.name] = false; break;
11435                 default : da[c.name] = ""; break;
11436             }
11437             
11438         });
11439         return new this.recordType(Roo.apply(da, d));
11440     }
11441     
11442 };/*
11443  * Based on:
11444  * Ext JS Library 1.1.1
11445  * Copyright(c) 2006-2007, Ext JS, LLC.
11446  *
11447  * Originally Released Under LGPL - original licence link has changed is not relivant.
11448  *
11449  * Fork - LGPL
11450  * <script type="text/javascript">
11451  */
11452
11453 /**
11454  * @class Roo.data.DataProxy
11455  * @extends Roo.data.Observable
11456  * This class is an abstract base class for implementations which provide retrieval of
11457  * unformatted data objects.<br>
11458  * <p>
11459  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11460  * (of the appropriate type which knows how to parse the data object) to provide a block of
11461  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11462  * <p>
11463  * Custom implementations must implement the load method as described in
11464  * {@link Roo.data.HttpProxy#load}.
11465  */
11466 Roo.data.DataProxy = function(){
11467     this.addEvents({
11468         /**
11469          * @event beforeload
11470          * Fires before a network request is made to retrieve a data object.
11471          * @param {Object} This DataProxy object.
11472          * @param {Object} params The params parameter to the load function.
11473          */
11474         beforeload : true,
11475         /**
11476          * @event load
11477          * Fires before the load method's callback is called.
11478          * @param {Object} This DataProxy object.
11479          * @param {Object} o The data object.
11480          * @param {Object} arg The callback argument object passed to the load function.
11481          */
11482         load : true,
11483         /**
11484          * @event loadexception
11485          * Fires if an Exception occurs during data retrieval.
11486          * @param {Object} This DataProxy object.
11487          * @param {Object} o The data object.
11488          * @param {Object} arg The callback argument object passed to the load function.
11489          * @param {Object} e The Exception.
11490          */
11491         loadexception : true
11492     });
11493     Roo.data.DataProxy.superclass.constructor.call(this);
11494 };
11495
11496 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11497
11498     /**
11499      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11500      */
11501 /*
11502  * Based on:
11503  * Ext JS Library 1.1.1
11504  * Copyright(c) 2006-2007, Ext JS, LLC.
11505  *
11506  * Originally Released Under LGPL - original licence link has changed is not relivant.
11507  *
11508  * Fork - LGPL
11509  * <script type="text/javascript">
11510  */
11511 /**
11512  * @class Roo.data.MemoryProxy
11513  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11514  * to the Reader when its load method is called.
11515  * @constructor
11516  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11517  */
11518 Roo.data.MemoryProxy = function(data){
11519     if (data.data) {
11520         data = data.data;
11521     }
11522     Roo.data.MemoryProxy.superclass.constructor.call(this);
11523     this.data = data;
11524 };
11525
11526 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11527     
11528     /**
11529      * Load data from the requested source (in this case an in-memory
11530      * data object passed to the constructor), read the data object into
11531      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11532      * process that block using the passed callback.
11533      * @param {Object} params This parameter is not used by the MemoryProxy class.
11534      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11535      * object into a block of Roo.data.Records.
11536      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11537      * The function must be passed <ul>
11538      * <li>The Record block object</li>
11539      * <li>The "arg" argument from the load function</li>
11540      * <li>A boolean success indicator</li>
11541      * </ul>
11542      * @param {Object} scope The scope in which to call the callback
11543      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11544      */
11545     load : function(params, reader, callback, scope, arg){
11546         params = params || {};
11547         var result;
11548         try {
11549             result = reader.readRecords(this.data);
11550         }catch(e){
11551             this.fireEvent("loadexception", this, arg, null, e);
11552             callback.call(scope, null, arg, false);
11553             return;
11554         }
11555         callback.call(scope, result, arg, true);
11556     },
11557     
11558     // private
11559     update : function(params, records){
11560         
11561     }
11562 });/*
11563  * Based on:
11564  * Ext JS Library 1.1.1
11565  * Copyright(c) 2006-2007, Ext JS, LLC.
11566  *
11567  * Originally Released Under LGPL - original licence link has changed is not relivant.
11568  *
11569  * Fork - LGPL
11570  * <script type="text/javascript">
11571  */
11572 /**
11573  * @class Roo.data.HttpProxy
11574  * @extends Roo.data.DataProxy
11575  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11576  * configured to reference a certain URL.<br><br>
11577  * <p>
11578  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11579  * from which the running page was served.<br><br>
11580  * <p>
11581  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11582  * <p>
11583  * Be aware that to enable the browser to parse an XML document, the server must set
11584  * the Content-Type header in the HTTP response to "text/xml".
11585  * @constructor
11586  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11587  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11588  * will be used to make the request.
11589  */
11590 Roo.data.HttpProxy = function(conn){
11591     Roo.data.HttpProxy.superclass.constructor.call(this);
11592     // is conn a conn config or a real conn?
11593     this.conn = conn;
11594     this.useAjax = !conn || !conn.events;
11595   
11596 };
11597
11598 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11599     // thse are take from connection...
11600     
11601     /**
11602      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11603      */
11604     /**
11605      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11606      * extra parameters to each request made by this object. (defaults to undefined)
11607      */
11608     /**
11609      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11610      *  to each request made by this object. (defaults to undefined)
11611      */
11612     /**
11613      * @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)
11614      */
11615     /**
11616      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11617      */
11618      /**
11619      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11620      * @type Boolean
11621      */
11622   
11623
11624     /**
11625      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11626      * @type Boolean
11627      */
11628     /**
11629      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11630      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11631      * a finer-grained basis than the DataProxy events.
11632      */
11633     getConnection : function(){
11634         return this.useAjax ? Roo.Ajax : this.conn;
11635     },
11636
11637     /**
11638      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11639      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11640      * process that block using the passed callback.
11641      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11642      * for the request to the remote server.
11643      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11644      * object into a block of Roo.data.Records.
11645      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11646      * The function must be passed <ul>
11647      * <li>The Record block object</li>
11648      * <li>The "arg" argument from the load function</li>
11649      * <li>A boolean success indicator</li>
11650      * </ul>
11651      * @param {Object} scope The scope in which to call the callback
11652      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11653      */
11654     load : function(params, reader, callback, scope, arg){
11655         if(this.fireEvent("beforeload", this, params) !== false){
11656             var  o = {
11657                 params : params || {},
11658                 request: {
11659                     callback : callback,
11660                     scope : scope,
11661                     arg : arg
11662                 },
11663                 reader: reader,
11664                 callback : this.loadResponse,
11665                 scope: this
11666             };
11667             if(this.useAjax){
11668                 Roo.applyIf(o, this.conn);
11669                 if(this.activeRequest){
11670                     Roo.Ajax.abort(this.activeRequest);
11671                 }
11672                 this.activeRequest = Roo.Ajax.request(o);
11673             }else{
11674                 this.conn.request(o);
11675             }
11676         }else{
11677             callback.call(scope||this, null, arg, false);
11678         }
11679     },
11680
11681     // private
11682     loadResponse : function(o, success, response){
11683         delete this.activeRequest;
11684         if(!success){
11685             this.fireEvent("loadexception", this, o, response);
11686             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11687             return;
11688         }
11689         var result;
11690         try {
11691             result = o.reader.read(response);
11692         }catch(e){
11693             this.fireEvent("loadexception", this, o, response, e);
11694             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11695             return;
11696         }
11697         
11698         this.fireEvent("load", this, o, o.request.arg);
11699         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11700     },
11701
11702     // private
11703     update : function(dataSet){
11704
11705     },
11706
11707     // private
11708     updateResponse : function(dataSet){
11709
11710     }
11711 });/*
11712  * Based on:
11713  * Ext JS Library 1.1.1
11714  * Copyright(c) 2006-2007, Ext JS, LLC.
11715  *
11716  * Originally Released Under LGPL - original licence link has changed is not relivant.
11717  *
11718  * Fork - LGPL
11719  * <script type="text/javascript">
11720  */
11721
11722 /**
11723  * @class Roo.data.ScriptTagProxy
11724  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11725  * other than the originating domain of the running page.<br><br>
11726  * <p>
11727  * <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
11728  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11729  * <p>
11730  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11731  * source code that is used as the source inside a &lt;script> tag.<br><br>
11732  * <p>
11733  * In order for the browser to process the returned data, the server must wrap the data object
11734  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11735  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11736  * depending on whether the callback name was passed:
11737  * <p>
11738  * <pre><code>
11739 boolean scriptTag = false;
11740 String cb = request.getParameter("callback");
11741 if (cb != null) {
11742     scriptTag = true;
11743     response.setContentType("text/javascript");
11744 } else {
11745     response.setContentType("application/x-json");
11746 }
11747 Writer out = response.getWriter();
11748 if (scriptTag) {
11749     out.write(cb + "(");
11750 }
11751 out.print(dataBlock.toJsonString());
11752 if (scriptTag) {
11753     out.write(");");
11754 }
11755 </pre></code>
11756  *
11757  * @constructor
11758  * @param {Object} config A configuration object.
11759  */
11760 Roo.data.ScriptTagProxy = function(config){
11761     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11762     Roo.apply(this, config);
11763     this.head = document.getElementsByTagName("head")[0];
11764 };
11765
11766 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11767
11768 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11769     /**
11770      * @cfg {String} url The URL from which to request the data object.
11771      */
11772     /**
11773      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11774      */
11775     timeout : 30000,
11776     /**
11777      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11778      * the server the name of the callback function set up by the load call to process the returned data object.
11779      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11780      * javascript output which calls this named function passing the data object as its only parameter.
11781      */
11782     callbackParam : "callback",
11783     /**
11784      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11785      * name to the request.
11786      */
11787     nocache : true,
11788
11789     /**
11790      * Load data from the configured URL, read the data object into
11791      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11792      * process that block using the passed callback.
11793      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11794      * for the request to the remote server.
11795      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11796      * object into a block of Roo.data.Records.
11797      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11798      * The function must be passed <ul>
11799      * <li>The Record block object</li>
11800      * <li>The "arg" argument from the load function</li>
11801      * <li>A boolean success indicator</li>
11802      * </ul>
11803      * @param {Object} scope The scope in which to call the callback
11804      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11805      */
11806     load : function(params, reader, callback, scope, arg){
11807         if(this.fireEvent("beforeload", this, params) !== false){
11808
11809             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11810
11811             var url = this.url;
11812             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11813             if(this.nocache){
11814                 url += "&_dc=" + (new Date().getTime());
11815             }
11816             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11817             var trans = {
11818                 id : transId,
11819                 cb : "stcCallback"+transId,
11820                 scriptId : "stcScript"+transId,
11821                 params : params,
11822                 arg : arg,
11823                 url : url,
11824                 callback : callback,
11825                 scope : scope,
11826                 reader : reader
11827             };
11828             var conn = this;
11829
11830             window[trans.cb] = function(o){
11831                 conn.handleResponse(o, trans);
11832             };
11833
11834             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11835
11836             if(this.autoAbort !== false){
11837                 this.abort();
11838             }
11839
11840             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11841
11842             var script = document.createElement("script");
11843             script.setAttribute("src", url);
11844             script.setAttribute("type", "text/javascript");
11845             script.setAttribute("id", trans.scriptId);
11846             this.head.appendChild(script);
11847
11848             this.trans = trans;
11849         }else{
11850             callback.call(scope||this, null, arg, false);
11851         }
11852     },
11853
11854     // private
11855     isLoading : function(){
11856         return this.trans ? true : false;
11857     },
11858
11859     /**
11860      * Abort the current server request.
11861      */
11862     abort : function(){
11863         if(this.isLoading()){
11864             this.destroyTrans(this.trans);
11865         }
11866     },
11867
11868     // private
11869     destroyTrans : function(trans, isLoaded){
11870         this.head.removeChild(document.getElementById(trans.scriptId));
11871         clearTimeout(trans.timeoutId);
11872         if(isLoaded){
11873             window[trans.cb] = undefined;
11874             try{
11875                 delete window[trans.cb];
11876             }catch(e){}
11877         }else{
11878             // if hasn't been loaded, wait for load to remove it to prevent script error
11879             window[trans.cb] = function(){
11880                 window[trans.cb] = undefined;
11881                 try{
11882                     delete window[trans.cb];
11883                 }catch(e){}
11884             };
11885         }
11886     },
11887
11888     // private
11889     handleResponse : function(o, trans){
11890         this.trans = false;
11891         this.destroyTrans(trans, true);
11892         var result;
11893         try {
11894             result = trans.reader.readRecords(o);
11895         }catch(e){
11896             this.fireEvent("loadexception", this, o, trans.arg, e);
11897             trans.callback.call(trans.scope||window, null, trans.arg, false);
11898             return;
11899         }
11900         this.fireEvent("load", this, o, trans.arg);
11901         trans.callback.call(trans.scope||window, result, trans.arg, true);
11902     },
11903
11904     // private
11905     handleFailure : function(trans){
11906         this.trans = false;
11907         this.destroyTrans(trans, false);
11908         this.fireEvent("loadexception", this, null, trans.arg);
11909         trans.callback.call(trans.scope||window, null, trans.arg, false);
11910     }
11911 });/*
11912  * Based on:
11913  * Ext JS Library 1.1.1
11914  * Copyright(c) 2006-2007, Ext JS, LLC.
11915  *
11916  * Originally Released Under LGPL - original licence link has changed is not relivant.
11917  *
11918  * Fork - LGPL
11919  * <script type="text/javascript">
11920  */
11921
11922 /**
11923  * @class Roo.data.JsonReader
11924  * @extends Roo.data.DataReader
11925  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11926  * based on mappings in a provided Roo.data.Record constructor.
11927  * 
11928  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11929  * in the reply previously. 
11930  * 
11931  * <p>
11932  * Example code:
11933  * <pre><code>
11934 var RecordDef = Roo.data.Record.create([
11935     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11936     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11937 ]);
11938 var myReader = new Roo.data.JsonReader({
11939     totalProperty: "results",    // The property which contains the total dataset size (optional)
11940     root: "rows",                // The property which contains an Array of row objects
11941     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11942 }, RecordDef);
11943 </code></pre>
11944  * <p>
11945  * This would consume a JSON file like this:
11946  * <pre><code>
11947 { 'results': 2, 'rows': [
11948     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11949     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11950 }
11951 </code></pre>
11952  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11953  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11954  * paged from the remote server.
11955  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11956  * @cfg {String} root name of the property which contains the Array of row objects.
11957  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11958  * @cfg {Array} fields Array of field definition objects
11959  * @constructor
11960  * Create a new JsonReader
11961  * @param {Object} meta Metadata configuration options
11962  * @param {Object} recordType Either an Array of field definition objects,
11963  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11964  */
11965 Roo.data.JsonReader = function(meta, recordType){
11966     
11967     meta = meta || {};
11968     // set some defaults:
11969     Roo.applyIf(meta, {
11970         totalProperty: 'total',
11971         successProperty : 'success',
11972         root : 'data',
11973         id : 'id'
11974     });
11975     
11976     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11977 };
11978 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11979     
11980     /**
11981      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11982      * Used by Store query builder to append _requestMeta to params.
11983      * 
11984      */
11985     metaFromRemote : false,
11986     /**
11987      * This method is only used by a DataProxy which has retrieved data from a remote server.
11988      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11989      * @return {Object} data A data block which is used by an Roo.data.Store object as
11990      * a cache of Roo.data.Records.
11991      */
11992     read : function(response){
11993         var json = response.responseText;
11994        
11995         var o = /* eval:var:o */ eval("("+json+")");
11996         if(!o) {
11997             throw {message: "JsonReader.read: Json object not found"};
11998         }
11999         
12000         if(o.metaData){
12001             
12002             delete this.ef;
12003             this.metaFromRemote = true;
12004             this.meta = o.metaData;
12005             this.recordType = Roo.data.Record.create(o.metaData.fields);
12006             this.onMetaChange(this.meta, this.recordType, o);
12007         }
12008         return this.readRecords(o);
12009     },
12010
12011     // private function a store will implement
12012     onMetaChange : function(meta, recordType, o){
12013
12014     },
12015
12016     /**
12017          * @ignore
12018          */
12019     simpleAccess: function(obj, subsc) {
12020         return obj[subsc];
12021     },
12022
12023         /**
12024          * @ignore
12025          */
12026     getJsonAccessor: function(){
12027         var re = /[\[\.]/;
12028         return function(expr) {
12029             try {
12030                 return(re.test(expr))
12031                     ? new Function("obj", "return obj." + expr)
12032                     : function(obj){
12033                         return obj[expr];
12034                     };
12035             } catch(e){}
12036             return Roo.emptyFn;
12037         };
12038     }(),
12039
12040     /**
12041      * Create a data block containing Roo.data.Records from an XML document.
12042      * @param {Object} o An object which contains an Array of row objects in the property specified
12043      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12044      * which contains the total size of the dataset.
12045      * @return {Object} data A data block which is used by an Roo.data.Store object as
12046      * a cache of Roo.data.Records.
12047      */
12048     readRecords : function(o){
12049         /**
12050          * After any data loads, the raw JSON data is available for further custom processing.
12051          * @type Object
12052          */
12053         this.o = o;
12054         var s = this.meta, Record = this.recordType,
12055             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12056
12057 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12058         if (!this.ef) {
12059             if(s.totalProperty) {
12060                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12061                 }
12062                 if(s.successProperty) {
12063                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12064                 }
12065                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12066                 if (s.id) {
12067                         var g = this.getJsonAccessor(s.id);
12068                         this.getId = function(rec) {
12069                                 var r = g(rec);  
12070                                 return (r === undefined || r === "") ? null : r;
12071                         };
12072                 } else {
12073                         this.getId = function(){return null;};
12074                 }
12075             this.ef = [];
12076             for(var jj = 0; jj < fl; jj++){
12077                 f = fi[jj];
12078                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12079                 this.ef[jj] = this.getJsonAccessor(map);
12080             }
12081         }
12082
12083         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12084         if(s.totalProperty){
12085             var vt = parseInt(this.getTotal(o), 10);
12086             if(!isNaN(vt)){
12087                 totalRecords = vt;
12088             }
12089         }
12090         if(s.successProperty){
12091             var vs = this.getSuccess(o);
12092             if(vs === false || vs === 'false'){
12093                 success = false;
12094             }
12095         }
12096         var records = [];
12097         for(var i = 0; i < c; i++){
12098                 var n = root[i];
12099             var values = {};
12100             var id = this.getId(n);
12101             for(var j = 0; j < fl; j++){
12102                 f = fi[j];
12103             var v = this.ef[j](n);
12104             if (!f.convert) {
12105                 Roo.log('missing convert for ' + f.name);
12106                 Roo.log(f);
12107                 continue;
12108             }
12109             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12110             }
12111             var record = new Record(values, id);
12112             record.json = n;
12113             records[i] = record;
12114         }
12115         return {
12116             raw : o,
12117             success : success,
12118             records : records,
12119             totalRecords : totalRecords
12120         };
12121     }
12122 });/*
12123  * Based on:
12124  * Ext JS Library 1.1.1
12125  * Copyright(c) 2006-2007, Ext JS, LLC.
12126  *
12127  * Originally Released Under LGPL - original licence link has changed is not relivant.
12128  *
12129  * Fork - LGPL
12130  * <script type="text/javascript">
12131  */
12132
12133 /**
12134  * @class Roo.data.ArrayReader
12135  * @extends Roo.data.DataReader
12136  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12137  * Each element of that Array represents a row of data fields. The
12138  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12139  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12140  * <p>
12141  * Example code:.
12142  * <pre><code>
12143 var RecordDef = Roo.data.Record.create([
12144     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12145     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12146 ]);
12147 var myReader = new Roo.data.ArrayReader({
12148     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12149 }, RecordDef);
12150 </code></pre>
12151  * <p>
12152  * This would consume an Array like this:
12153  * <pre><code>
12154 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12155   </code></pre>
12156  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12157  * @constructor
12158  * Create a new JsonReader
12159  * @param {Object} meta Metadata configuration options.
12160  * @param {Object} recordType Either an Array of field definition objects
12161  * as specified to {@link Roo.data.Record#create},
12162  * or an {@link Roo.data.Record} object
12163  * created using {@link Roo.data.Record#create}.
12164  */
12165 Roo.data.ArrayReader = function(meta, recordType){
12166     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12167 };
12168
12169 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12170     /**
12171      * Create a data block containing Roo.data.Records from an XML document.
12172      * @param {Object} o An Array of row objects which represents the dataset.
12173      * @return {Object} data A data block which is used by an Roo.data.Store object as
12174      * a cache of Roo.data.Records.
12175      */
12176     readRecords : function(o){
12177         var sid = this.meta ? this.meta.id : null;
12178         var recordType = this.recordType, fields = recordType.prototype.fields;
12179         var records = [];
12180         var root = o;
12181             for(var i = 0; i < root.length; i++){
12182                     var n = root[i];
12183                 var values = {};
12184                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12185                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12186                 var f = fields.items[j];
12187                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12188                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12189                 v = f.convert(v);
12190                 values[f.name] = v;
12191             }
12192                 var record = new recordType(values, id);
12193                 record.json = n;
12194                 records[records.length] = record;
12195             }
12196             return {
12197                 records : records,
12198                 totalRecords : records.length
12199             };
12200     }
12201 });/*
12202  * - LGPL
12203  * * 
12204  */
12205
12206 /**
12207  * @class Roo.bootstrap.ComboBox
12208  * @extends Roo.bootstrap.TriggerField
12209  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12210  * @cfg {Boolean} append (true|false) default false
12211  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12212  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12213  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12214  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12215  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12216  * @cfg {Boolean} animate default true
12217  * @cfg {Boolean} emptyResultText only for touch device
12218  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12219  * @constructor
12220  * Create a new ComboBox.
12221  * @param {Object} config Configuration options
12222  */
12223 Roo.bootstrap.ComboBox = function(config){
12224     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12225     this.addEvents({
12226         /**
12227          * @event expand
12228          * Fires when the dropdown list is expanded
12229              * @param {Roo.bootstrap.ComboBox} combo This combo box
12230              */
12231         'expand' : true,
12232         /**
12233          * @event collapse
12234          * Fires when the dropdown list is collapsed
12235              * @param {Roo.bootstrap.ComboBox} combo This combo box
12236              */
12237         'collapse' : true,
12238         /**
12239          * @event beforeselect
12240          * Fires before a list item is selected. Return false to cancel the selection.
12241              * @param {Roo.bootstrap.ComboBox} combo This combo box
12242              * @param {Roo.data.Record} record The data record returned from the underlying store
12243              * @param {Number} index The index of the selected item in the dropdown list
12244              */
12245         'beforeselect' : true,
12246         /**
12247          * @event select
12248          * Fires when a list item is selected
12249              * @param {Roo.bootstrap.ComboBox} combo This combo box
12250              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12251              * @param {Number} index The index of the selected item in the dropdown list
12252              */
12253         'select' : true,
12254         /**
12255          * @event beforequery
12256          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12257          * The event object passed has these properties:
12258              * @param {Roo.bootstrap.ComboBox} combo This combo box
12259              * @param {String} query The query
12260              * @param {Boolean} forceAll true to force "all" query
12261              * @param {Boolean} cancel true to cancel the query
12262              * @param {Object} e The query event object
12263              */
12264         'beforequery': true,
12265          /**
12266          * @event add
12267          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12268              * @param {Roo.bootstrap.ComboBox} combo This combo box
12269              */
12270         'add' : true,
12271         /**
12272          * @event edit
12273          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12274              * @param {Roo.bootstrap.ComboBox} combo This combo box
12275              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12276              */
12277         'edit' : true,
12278         /**
12279          * @event remove
12280          * Fires when the remove value from the combobox array
12281              * @param {Roo.bootstrap.ComboBox} combo This combo box
12282              */
12283         'remove' : true,
12284         /**
12285          * @event afterremove
12286          * Fires when the remove value from the combobox array
12287              * @param {Roo.bootstrap.ComboBox} combo This combo box
12288              */
12289         'afterremove' : true,
12290         /**
12291          * @event specialfilter
12292          * Fires when specialfilter
12293             * @param {Roo.bootstrap.ComboBox} combo This combo box
12294             */
12295         'specialfilter' : true,
12296         /**
12297          * @event tick
12298          * Fires when tick the element
12299             * @param {Roo.bootstrap.ComboBox} combo This combo box
12300             */
12301         'tick' : true,
12302         /**
12303          * @event touchviewdisplay
12304          * Fires when touch view require special display (default is using displayField)
12305             * @param {Roo.bootstrap.ComboBox} combo This combo box
12306             * @param {Object} cfg set html .
12307             */
12308         'touchviewdisplay' : true
12309         
12310     });
12311     
12312     this.item = [];
12313     this.tickItems = [];
12314     
12315     this.selectedIndex = -1;
12316     if(this.mode == 'local'){
12317         if(config.queryDelay === undefined){
12318             this.queryDelay = 10;
12319         }
12320         if(config.minChars === undefined){
12321             this.minChars = 0;
12322         }
12323     }
12324 };
12325
12326 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12327      
12328     /**
12329      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12330      * rendering into an Roo.Editor, defaults to false)
12331      */
12332     /**
12333      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12334      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12335      */
12336     /**
12337      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12338      */
12339     /**
12340      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12341      * the dropdown list (defaults to undefined, with no header element)
12342      */
12343
12344      /**
12345      * @cfg {String/Roo.Template} tpl The template to use to render the output
12346      */
12347      
12348      /**
12349      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12350      */
12351     listWidth: undefined,
12352     /**
12353      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12354      * mode = 'remote' or 'text' if mode = 'local')
12355      */
12356     displayField: undefined,
12357     
12358     /**
12359      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12360      * mode = 'remote' or 'value' if mode = 'local'). 
12361      * Note: use of a valueField requires the user make a selection
12362      * in order for a value to be mapped.
12363      */
12364     valueField: undefined,
12365     /**
12366      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12367      */
12368     modalTitle : '',
12369     
12370     /**
12371      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12372      * field's data value (defaults to the underlying DOM element's name)
12373      */
12374     hiddenName: undefined,
12375     /**
12376      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12377      */
12378     listClass: '',
12379     /**
12380      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12381      */
12382     selectedClass: 'active',
12383     
12384     /**
12385      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12386      */
12387     shadow:'sides',
12388     /**
12389      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12390      * anchor positions (defaults to 'tl-bl')
12391      */
12392     listAlign: 'tl-bl?',
12393     /**
12394      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12395      */
12396     maxHeight: 300,
12397     /**
12398      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12399      * query specified by the allQuery config option (defaults to 'query')
12400      */
12401     triggerAction: 'query',
12402     /**
12403      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12404      * (defaults to 4, does not apply if editable = false)
12405      */
12406     minChars : 4,
12407     /**
12408      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12409      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12410      */
12411     typeAhead: false,
12412     /**
12413      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12414      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12415      */
12416     queryDelay: 500,
12417     /**
12418      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12419      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12420      */
12421     pageSize: 0,
12422     /**
12423      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12424      * when editable = true (defaults to false)
12425      */
12426     selectOnFocus:false,
12427     /**
12428      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12429      */
12430     queryParam: 'query',
12431     /**
12432      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12433      * when mode = 'remote' (defaults to 'Loading...')
12434      */
12435     loadingText: 'Loading...',
12436     /**
12437      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12438      */
12439     resizable: false,
12440     /**
12441      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12442      */
12443     handleHeight : 8,
12444     /**
12445      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12446      * traditional select (defaults to true)
12447      */
12448     editable: true,
12449     /**
12450      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12451      */
12452     allQuery: '',
12453     /**
12454      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12455      */
12456     mode: 'remote',
12457     /**
12458      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12459      * listWidth has a higher value)
12460      */
12461     minListWidth : 70,
12462     /**
12463      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12464      * allow the user to set arbitrary text into the field (defaults to false)
12465      */
12466     forceSelection:false,
12467     /**
12468      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12469      * if typeAhead = true (defaults to 250)
12470      */
12471     typeAheadDelay : 250,
12472     /**
12473      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12474      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12475      */
12476     valueNotFoundText : undefined,
12477     /**
12478      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12479      */
12480     blockFocus : false,
12481     
12482     /**
12483      * @cfg {Boolean} disableClear Disable showing of clear button.
12484      */
12485     disableClear : false,
12486     /**
12487      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12488      */
12489     alwaysQuery : false,
12490     
12491     /**
12492      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12493      */
12494     multiple : false,
12495     
12496     /**
12497      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12498      */
12499     invalidClass : "has-warning",
12500     
12501     /**
12502      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12503      */
12504     validClass : "has-success",
12505     
12506     /**
12507      * @cfg {Boolean} specialFilter (true|false) special filter default false
12508      */
12509     specialFilter : false,
12510     
12511     /**
12512      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12513      */
12514     mobileTouchView : true,
12515     
12516     /**
12517      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12518      */
12519     useNativeIOS : false,
12520     
12521     ios_options : false,
12522     
12523     //private
12524     addicon : false,
12525     editicon: false,
12526     
12527     page: 0,
12528     hasQuery: false,
12529     append: false,
12530     loadNext: false,
12531     autoFocus : true,
12532     tickable : false,
12533     btnPosition : 'right',
12534     triggerList : true,
12535     showToggleBtn : true,
12536     animate : true,
12537     emptyResultText: 'Empty',
12538     triggerText : 'Select',
12539     
12540     // element that contains real text value.. (when hidden is used..)
12541     
12542     getAutoCreate : function()
12543     {
12544         var cfg = false;
12545         
12546         /*
12547          * Render classic select for iso
12548          */
12549         
12550         if(Roo.isIOS && this.useNativeIOS){
12551             cfg = this.getAutoCreateNativeIOS();
12552             return cfg;
12553         }
12554         
12555         /*
12556          * Touch Devices
12557          */
12558         
12559         if(Roo.isTouch && this.mobileTouchView){
12560             cfg = this.getAutoCreateTouchView();
12561             return cfg;;
12562         }
12563         
12564         /*
12565          *  Normal ComboBox
12566          */
12567         if(!this.tickable){
12568             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12569             return cfg;
12570         }
12571         
12572         /*
12573          *  ComboBox with tickable selections
12574          */
12575              
12576         var align = this.labelAlign || this.parentLabelAlign();
12577         
12578         cfg = {
12579             cls : 'form-group roo-combobox-tickable' //input-group
12580         };
12581         
12582         var btn_text_select = '';
12583         var btn_text_done = '';
12584         var btn_text_cancel = '';
12585         
12586         if (this.btn_text_show) {
12587             btn_text_select = 'Select';
12588             btn_text_done = 'Done';
12589             btn_text_cancel = 'Cancel'; 
12590         }
12591         
12592         var buttons = {
12593             tag : 'div',
12594             cls : 'tickable-buttons',
12595             cn : [
12596                 {
12597                     tag : 'button',
12598                     type : 'button',
12599                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12600                     //html : this.triggerText
12601                     html: btn_text_select
12602                 },
12603                 {
12604                     tag : 'button',
12605                     type : 'button',
12606                     name : 'ok',
12607                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12608                     //html : 'Done'
12609                     html: btn_text_done
12610                 },
12611                 {
12612                     tag : 'button',
12613                     type : 'button',
12614                     name : 'cancel',
12615                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12616                     //html : 'Cancel'
12617                     html: btn_text_cancel
12618                 }
12619             ]
12620         };
12621         
12622         if(this.editable){
12623             buttons.cn.unshift({
12624                 tag: 'input',
12625                 cls: 'roo-select2-search-field-input'
12626             });
12627         }
12628         
12629         var _this = this;
12630         
12631         Roo.each(buttons.cn, function(c){
12632             if (_this.size) {
12633                 c.cls += ' btn-' + _this.size;
12634             }
12635
12636             if (_this.disabled) {
12637                 c.disabled = true;
12638             }
12639         });
12640         
12641         var box = {
12642             tag: 'div',
12643             cn: [
12644                 {
12645                     tag: 'input',
12646                     type : 'hidden',
12647                     cls: 'form-hidden-field'
12648                 },
12649                 {
12650                     tag: 'ul',
12651                     cls: 'roo-select2-choices',
12652                     cn:[
12653                         {
12654                             tag: 'li',
12655                             cls: 'roo-select2-search-field',
12656                             cn: [
12657
12658                                 buttons
12659                             ]
12660                         }
12661                     ]
12662                 }
12663             ]
12664         };
12665         
12666         var combobox = {
12667             cls: 'roo-select2-container input-group roo-select2-container-multi',
12668             cn: [
12669                 box
12670 //                {
12671 //                    tag: 'ul',
12672 //                    cls: 'typeahead typeahead-long dropdown-menu',
12673 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12674 //                }
12675             ]
12676         };
12677         
12678         if(this.hasFeedback && !this.allowBlank){
12679             
12680             var feedback = {
12681                 tag: 'span',
12682                 cls: 'glyphicon form-control-feedback'
12683             };
12684
12685             combobox.cn.push(feedback);
12686         }
12687         
12688         if (align ==='left' && this.fieldLabel.length) {
12689             
12690             cfg.cn = [
12691                 {
12692                     tag : 'i',
12693                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12694                     tooltip : 'This field is required'
12695                 },
12696                 {
12697                     tag: 'label',
12698                     'for' :  id,
12699                     cls : 'control-label',
12700                     html : this.fieldLabel
12701
12702                 },
12703                 {
12704                     cls : "", 
12705                     cn: [
12706                         combobox
12707                     ]
12708                 }
12709
12710             ];
12711             
12712             var labelCfg = cfg.cn[1];
12713             var contentCfg = cfg.cn[2];
12714             
12715
12716             if(this.indicatorpos == 'right'){
12717                 
12718                 cfg.cn = [
12719                     {
12720                         tag: 'label',
12721                         'for' :  id,
12722                         cls : 'control-label',
12723                         html : this.fieldLabel
12724
12725                     },
12726                     {
12727                         tag : 'i',
12728                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12729                         tooltip : 'This field is required'
12730                     },
12731                     {
12732                         cls : "",
12733                         cn: [
12734                             combobox
12735                         ]
12736                     }
12737
12738                 ];
12739                 
12740                 labelCfg = cfg.cn[0];
12741             
12742             }
12743             
12744             if(this.labelWidth > 12){
12745                 labelCfg.style = "width: " + this.labelWidth + 'px';
12746             }
12747             
12748             if(this.labelWidth < 13 && this.labelmd == 0){
12749                 this.labelmd = this.labelWidth;
12750             }
12751             
12752             if(this.labellg > 0){
12753                 labelCfg.cls += ' col-lg-' + this.labellg;
12754                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12755             }
12756             
12757             if(this.labelmd > 0){
12758                 labelCfg.cls += ' col-md-' + this.labelmd;
12759                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12760             }
12761             
12762             if(this.labelsm > 0){
12763                 labelCfg.cls += ' col-sm-' + this.labelsm;
12764                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12765             }
12766             
12767             if(this.labelxs > 0){
12768                 labelCfg.cls += ' col-xs-' + this.labelxs;
12769                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12770             }
12771                 
12772                 
12773         } else if ( this.fieldLabel.length) {
12774 //                Roo.log(" label");
12775                  cfg.cn = [
12776                     {
12777                         tag : 'i',
12778                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12779                         tooltip : 'This field is required'
12780                     },
12781                     {
12782                         tag: 'label',
12783                         //cls : 'input-group-addon',
12784                         html : this.fieldLabel
12785                         
12786                     },
12787                     
12788                     combobox
12789                     
12790                 ];
12791                 
12792                 if(this.indicatorpos == 'right'){
12793                     
12794                     cfg.cn = [
12795                         {
12796                             tag: 'label',
12797                             //cls : 'input-group-addon',
12798                             html : this.fieldLabel
12799
12800                         },
12801                         
12802                         {
12803                             tag : 'i',
12804                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12805                             tooltip : 'This field is required'
12806                         },
12807                         
12808                         combobox
12809
12810                     ];
12811                 
12812                 }
12813
12814         } else {
12815             
12816 //                Roo.log(" no label && no align");
12817                 cfg = combobox
12818                      
12819                 
12820         }
12821          
12822         var settings=this;
12823         ['xs','sm','md','lg'].map(function(size){
12824             if (settings[size]) {
12825                 cfg.cls += ' col-' + size + '-' + settings[size];
12826             }
12827         });
12828         
12829         return cfg;
12830         
12831     },
12832     
12833     _initEventsCalled : false,
12834     
12835     // private
12836     initEvents: function()
12837     {   
12838         if (this._initEventsCalled) { // as we call render... prevent looping...
12839             return;
12840         }
12841         this._initEventsCalled = true;
12842         
12843         if (!this.store) {
12844             throw "can not find store for combo";
12845         }
12846         
12847         this.store = Roo.factory(this.store, Roo.data);
12848         
12849         // if we are building from html. then this element is so complex, that we can not really
12850         // use the rendered HTML.
12851         // so we have to trash and replace the previous code.
12852         if (Roo.XComponent.build_from_html) {
12853             
12854             // remove this element....
12855             var e = this.el.dom, k=0;
12856             while (e ) { e = e.previousSibling;  ++k;}
12857
12858             this.el.remove();
12859             
12860             this.el=false;
12861             this.rendered = false;
12862             
12863             this.render(this.parent().getChildContainer(true), k);
12864             
12865             
12866             
12867         }
12868         
12869         if(Roo.isIOS && this.useNativeIOS){
12870             this.initIOSView();
12871             return;
12872         }
12873         
12874         /*
12875          * Touch Devices
12876          */
12877         
12878         if(Roo.isTouch && this.mobileTouchView){
12879             this.initTouchView();
12880             return;
12881         }
12882         
12883         if(this.tickable){
12884             this.initTickableEvents();
12885             return;
12886         }
12887         
12888         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12889         
12890         if(this.hiddenName){
12891             
12892             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12893             
12894             this.hiddenField.dom.value =
12895                 this.hiddenValue !== undefined ? this.hiddenValue :
12896                 this.value !== undefined ? this.value : '';
12897
12898             // prevent input submission
12899             this.el.dom.removeAttribute('name');
12900             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12901              
12902              
12903         }
12904         //if(Roo.isGecko){
12905         //    this.el.dom.setAttribute('autocomplete', 'off');
12906         //}
12907         
12908         var cls = 'x-combo-list';
12909         
12910         //this.list = new Roo.Layer({
12911         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12912         //});
12913         
12914         var _this = this;
12915         
12916         (function(){
12917             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12918             _this.list.setWidth(lw);
12919         }).defer(100);
12920         
12921         this.list.on('mouseover', this.onViewOver, this);
12922         this.list.on('mousemove', this.onViewMove, this);
12923         
12924         this.list.on('scroll', this.onViewScroll, this);
12925         
12926         /*
12927         this.list.swallowEvent('mousewheel');
12928         this.assetHeight = 0;
12929
12930         if(this.title){
12931             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12932             this.assetHeight += this.header.getHeight();
12933         }
12934
12935         this.innerList = this.list.createChild({cls:cls+'-inner'});
12936         this.innerList.on('mouseover', this.onViewOver, this);
12937         this.innerList.on('mousemove', this.onViewMove, this);
12938         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12939         
12940         if(this.allowBlank && !this.pageSize && !this.disableClear){
12941             this.footer = this.list.createChild({cls:cls+'-ft'});
12942             this.pageTb = new Roo.Toolbar(this.footer);
12943            
12944         }
12945         if(this.pageSize){
12946             this.footer = this.list.createChild({cls:cls+'-ft'});
12947             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12948                     {pageSize: this.pageSize});
12949             
12950         }
12951         
12952         if (this.pageTb && this.allowBlank && !this.disableClear) {
12953             var _this = this;
12954             this.pageTb.add(new Roo.Toolbar.Fill(), {
12955                 cls: 'x-btn-icon x-btn-clear',
12956                 text: '&#160;',
12957                 handler: function()
12958                 {
12959                     _this.collapse();
12960                     _this.clearValue();
12961                     _this.onSelect(false, -1);
12962                 }
12963             });
12964         }
12965         if (this.footer) {
12966             this.assetHeight += this.footer.getHeight();
12967         }
12968         */
12969             
12970         if(!this.tpl){
12971             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12972         }
12973
12974         this.view = new Roo.View(this.list, this.tpl, {
12975             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12976         });
12977         //this.view.wrapEl.setDisplayed(false);
12978         this.view.on('click', this.onViewClick, this);
12979         
12980         
12981         
12982         this.store.on('beforeload', this.onBeforeLoad, this);
12983         this.store.on('load', this.onLoad, this);
12984         this.store.on('loadexception', this.onLoadException, this);
12985         /*
12986         if(this.resizable){
12987             this.resizer = new Roo.Resizable(this.list,  {
12988                pinned:true, handles:'se'
12989             });
12990             this.resizer.on('resize', function(r, w, h){
12991                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12992                 this.listWidth = w;
12993                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12994                 this.restrictHeight();
12995             }, this);
12996             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12997         }
12998         */
12999         if(!this.editable){
13000             this.editable = true;
13001             this.setEditable(false);
13002         }
13003         
13004         /*
13005         
13006         if (typeof(this.events.add.listeners) != 'undefined') {
13007             
13008             this.addicon = this.wrap.createChild(
13009                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13010        
13011             this.addicon.on('click', function(e) {
13012                 this.fireEvent('add', this);
13013             }, this);
13014         }
13015         if (typeof(this.events.edit.listeners) != 'undefined') {
13016             
13017             this.editicon = this.wrap.createChild(
13018                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13019             if (this.addicon) {
13020                 this.editicon.setStyle('margin-left', '40px');
13021             }
13022             this.editicon.on('click', function(e) {
13023                 
13024                 // we fire even  if inothing is selected..
13025                 this.fireEvent('edit', this, this.lastData );
13026                 
13027             }, this);
13028         }
13029         */
13030         
13031         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13032             "up" : function(e){
13033                 this.inKeyMode = true;
13034                 this.selectPrev();
13035             },
13036
13037             "down" : function(e){
13038                 if(!this.isExpanded()){
13039                     this.onTriggerClick();
13040                 }else{
13041                     this.inKeyMode = true;
13042                     this.selectNext();
13043                 }
13044             },
13045
13046             "enter" : function(e){
13047 //                this.onViewClick();
13048                 //return true;
13049                 this.collapse();
13050                 
13051                 if(this.fireEvent("specialkey", this, e)){
13052                     this.onViewClick(false);
13053                 }
13054                 
13055                 return true;
13056             },
13057
13058             "esc" : function(e){
13059                 this.collapse();
13060             },
13061
13062             "tab" : function(e){
13063                 this.collapse();
13064                 
13065                 if(this.fireEvent("specialkey", this, e)){
13066                     this.onViewClick(false);
13067                 }
13068                 
13069                 return true;
13070             },
13071
13072             scope : this,
13073
13074             doRelay : function(foo, bar, hname){
13075                 if(hname == 'down' || this.scope.isExpanded()){
13076                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13077                 }
13078                 return true;
13079             },
13080
13081             forceKeyDown: true
13082         });
13083         
13084         
13085         this.queryDelay = Math.max(this.queryDelay || 10,
13086                 this.mode == 'local' ? 10 : 250);
13087         
13088         
13089         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13090         
13091         if(this.typeAhead){
13092             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13093         }
13094         if(this.editable !== false){
13095             this.inputEl().on("keyup", this.onKeyUp, this);
13096         }
13097         if(this.forceSelection){
13098             this.inputEl().on('blur', this.doForce, this);
13099         }
13100         
13101         if(this.multiple){
13102             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13103             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13104         }
13105     },
13106     
13107     initTickableEvents: function()
13108     {   
13109         this.createList();
13110         
13111         if(this.hiddenName){
13112             
13113             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13114             
13115             this.hiddenField.dom.value =
13116                 this.hiddenValue !== undefined ? this.hiddenValue :
13117                 this.value !== undefined ? this.value : '';
13118
13119             // prevent input submission
13120             this.el.dom.removeAttribute('name');
13121             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13122              
13123              
13124         }
13125         
13126 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13127         
13128         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13129         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13130         if(this.triggerList){
13131             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13132         }
13133          
13134         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13135         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13136         
13137         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13138         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13139         
13140         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13141         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13142         
13143         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13144         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13145         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13146         
13147         this.okBtn.hide();
13148         this.cancelBtn.hide();
13149         
13150         var _this = this;
13151         
13152         (function(){
13153             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13154             _this.list.setWidth(lw);
13155         }).defer(100);
13156         
13157         this.list.on('mouseover', this.onViewOver, this);
13158         this.list.on('mousemove', this.onViewMove, this);
13159         
13160         this.list.on('scroll', this.onViewScroll, this);
13161         
13162         if(!this.tpl){
13163             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>';
13164         }
13165
13166         this.view = new Roo.View(this.list, this.tpl, {
13167             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13168         });
13169         
13170         //this.view.wrapEl.setDisplayed(false);
13171         this.view.on('click', this.onViewClick, this);
13172         
13173         
13174         
13175         this.store.on('beforeload', this.onBeforeLoad, this);
13176         this.store.on('load', this.onLoad, this);
13177         this.store.on('loadexception', this.onLoadException, this);
13178         
13179         if(this.editable){
13180             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13181                 "up" : function(e){
13182                     this.inKeyMode = true;
13183                     this.selectPrev();
13184                 },
13185
13186                 "down" : function(e){
13187                     this.inKeyMode = true;
13188                     this.selectNext();
13189                 },
13190
13191                 "enter" : function(e){
13192                     if(this.fireEvent("specialkey", this, e)){
13193                         this.onViewClick(false);
13194                     }
13195                     
13196                     return true;
13197                 },
13198
13199                 "esc" : function(e){
13200                     this.onTickableFooterButtonClick(e, false, false);
13201                 },
13202
13203                 "tab" : function(e){
13204                     this.fireEvent("specialkey", this, e);
13205                     
13206                     this.onTickableFooterButtonClick(e, false, false);
13207                     
13208                     return true;
13209                 },
13210
13211                 scope : this,
13212
13213                 doRelay : function(e, fn, key){
13214                     if(this.scope.isExpanded()){
13215                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13216                     }
13217                     return true;
13218                 },
13219
13220                 forceKeyDown: true
13221             });
13222         }
13223         
13224         this.queryDelay = Math.max(this.queryDelay || 10,
13225                 this.mode == 'local' ? 10 : 250);
13226         
13227         
13228         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13229         
13230         if(this.typeAhead){
13231             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13232         }
13233         
13234         if(this.editable !== false){
13235             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13236         }
13237         
13238     },
13239
13240     onDestroy : function(){
13241         if(this.view){
13242             this.view.setStore(null);
13243             this.view.el.removeAllListeners();
13244             this.view.el.remove();
13245             this.view.purgeListeners();
13246         }
13247         if(this.list){
13248             this.list.dom.innerHTML  = '';
13249         }
13250         
13251         if(this.store){
13252             this.store.un('beforeload', this.onBeforeLoad, this);
13253             this.store.un('load', this.onLoad, this);
13254             this.store.un('loadexception', this.onLoadException, this);
13255         }
13256         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13257     },
13258
13259     // private
13260     fireKey : function(e){
13261         if(e.isNavKeyPress() && !this.list.isVisible()){
13262             this.fireEvent("specialkey", this, e);
13263         }
13264     },
13265
13266     // private
13267     onResize: function(w, h){
13268 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13269 //        
13270 //        if(typeof w != 'number'){
13271 //            // we do not handle it!?!?
13272 //            return;
13273 //        }
13274 //        var tw = this.trigger.getWidth();
13275 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13276 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13277 //        var x = w - tw;
13278 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13279 //            
13280 //        //this.trigger.setStyle('left', x+'px');
13281 //        
13282 //        if(this.list && this.listWidth === undefined){
13283 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13284 //            this.list.setWidth(lw);
13285 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13286 //        }
13287         
13288     
13289         
13290     },
13291
13292     /**
13293      * Allow or prevent the user from directly editing the field text.  If false is passed,
13294      * the user will only be able to select from the items defined in the dropdown list.  This method
13295      * is the runtime equivalent of setting the 'editable' config option at config time.
13296      * @param {Boolean} value True to allow the user to directly edit the field text
13297      */
13298     setEditable : function(value){
13299         if(value == this.editable){
13300             return;
13301         }
13302         this.editable = value;
13303         if(!value){
13304             this.inputEl().dom.setAttribute('readOnly', true);
13305             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13306             this.inputEl().addClass('x-combo-noedit');
13307         }else{
13308             this.inputEl().dom.setAttribute('readOnly', false);
13309             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13310             this.inputEl().removeClass('x-combo-noedit');
13311         }
13312     },
13313
13314     // private
13315     
13316     onBeforeLoad : function(combo,opts){
13317         if(!this.hasFocus){
13318             return;
13319         }
13320          if (!opts.add) {
13321             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13322          }
13323         this.restrictHeight();
13324         this.selectedIndex = -1;
13325     },
13326
13327     // private
13328     onLoad : function(){
13329         
13330         this.hasQuery = false;
13331         
13332         if(!this.hasFocus){
13333             return;
13334         }
13335         
13336         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13337             this.loading.hide();
13338         }
13339              
13340         if(this.store.getCount() > 0){
13341             this.expand();
13342             this.restrictHeight();
13343             if(this.lastQuery == this.allQuery){
13344                 if(this.editable && !this.tickable){
13345                     this.inputEl().dom.select();
13346                 }
13347                 
13348                 if(
13349                     !this.selectByValue(this.value, true) &&
13350                     this.autoFocus && 
13351                     (
13352                         !this.store.lastOptions ||
13353                         typeof(this.store.lastOptions.add) == 'undefined' || 
13354                         this.store.lastOptions.add != true
13355                     )
13356                 ){
13357                     this.select(0, true);
13358                 }
13359             }else{
13360                 if(this.autoFocus){
13361                     this.selectNext();
13362                 }
13363                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13364                     this.taTask.delay(this.typeAheadDelay);
13365                 }
13366             }
13367         }else{
13368             this.onEmptyResults();
13369         }
13370         
13371         //this.el.focus();
13372     },
13373     // private
13374     onLoadException : function()
13375     {
13376         this.hasQuery = false;
13377         
13378         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13379             this.loading.hide();
13380         }
13381         
13382         if(this.tickable && this.editable){
13383             return;
13384         }
13385         
13386         this.collapse();
13387         // only causes errors at present
13388         //Roo.log(this.store.reader.jsonData);
13389         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13390             // fixme
13391             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13392         //}
13393         
13394         
13395     },
13396     // private
13397     onTypeAhead : function(){
13398         if(this.store.getCount() > 0){
13399             var r = this.store.getAt(0);
13400             var newValue = r.data[this.displayField];
13401             var len = newValue.length;
13402             var selStart = this.getRawValue().length;
13403             
13404             if(selStart != len){
13405                 this.setRawValue(newValue);
13406                 this.selectText(selStart, newValue.length);
13407             }
13408         }
13409     },
13410
13411     // private
13412     onSelect : function(record, index){
13413         
13414         if(this.fireEvent('beforeselect', this, record, index) !== false){
13415         
13416             this.setFromData(index > -1 ? record.data : false);
13417             
13418             this.collapse();
13419             this.fireEvent('select', this, record, index);
13420         }
13421     },
13422
13423     /**
13424      * Returns the currently selected field value or empty string if no value is set.
13425      * @return {String} value The selected value
13426      */
13427     getValue : function()
13428     {
13429         if(Roo.isIOS && this.useNativeIOS){
13430             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13431         }
13432         
13433         if(this.multiple){
13434             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13435         }
13436         
13437         if(this.valueField){
13438             return typeof this.value != 'undefined' ? this.value : '';
13439         }else{
13440             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13441         }
13442     },
13443     
13444     getRawValue : function()
13445     {
13446         if(Roo.isIOS && this.useNativeIOS){
13447             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13448         }
13449         
13450         var v = this.inputEl().getValue();
13451         
13452         return v;
13453     },
13454
13455     /**
13456      * Clears any text/value currently set in the field
13457      */
13458     clearValue : function(){
13459         
13460         if(this.hiddenField){
13461             this.hiddenField.dom.value = '';
13462         }
13463         this.value = '';
13464         this.setRawValue('');
13465         this.lastSelectionText = '';
13466         this.lastData = false;
13467         
13468         var close = this.closeTriggerEl();
13469         
13470         if(close){
13471             close.hide();
13472         }
13473         
13474         this.validate();
13475         
13476     },
13477
13478     /**
13479      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13480      * will be displayed in the field.  If the value does not match the data value of an existing item,
13481      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13482      * Otherwise the field will be blank (although the value will still be set).
13483      * @param {String} value The value to match
13484      */
13485     setValue : function(v)
13486     {
13487         if(Roo.isIOS && this.useNativeIOS){
13488             this.setIOSValue(v);
13489             return;
13490         }
13491         
13492         if(this.multiple){
13493             this.syncValue();
13494             return;
13495         }
13496         
13497         var text = v;
13498         if(this.valueField){
13499             var r = this.findRecord(this.valueField, v);
13500             if(r){
13501                 text = r.data[this.displayField];
13502             }else if(this.valueNotFoundText !== undefined){
13503                 text = this.valueNotFoundText;
13504             }
13505         }
13506         this.lastSelectionText = text;
13507         if(this.hiddenField){
13508             this.hiddenField.dom.value = v;
13509         }
13510         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13511         this.value = v;
13512         
13513         var close = this.closeTriggerEl();
13514         
13515         if(close){
13516             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13517         }
13518         
13519         this.validate();
13520     },
13521     /**
13522      * @property {Object} the last set data for the element
13523      */
13524     
13525     lastData : false,
13526     /**
13527      * Sets the value of the field based on a object which is related to the record format for the store.
13528      * @param {Object} value the value to set as. or false on reset?
13529      */
13530     setFromData : function(o){
13531         
13532         if(this.multiple){
13533             this.addItem(o);
13534             return;
13535         }
13536             
13537         var dv = ''; // display value
13538         var vv = ''; // value value..
13539         this.lastData = o;
13540         if (this.displayField) {
13541             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13542         } else {
13543             // this is an error condition!!!
13544             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13545         }
13546         
13547         if(this.valueField){
13548             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13549         }
13550         
13551         var close = this.closeTriggerEl();
13552         
13553         if(close){
13554             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13555         }
13556         
13557         if(this.hiddenField){
13558             this.hiddenField.dom.value = vv;
13559             
13560             this.lastSelectionText = dv;
13561             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13562             this.value = vv;
13563             return;
13564         }
13565         // no hidden field.. - we store the value in 'value', but still display
13566         // display field!!!!
13567         this.lastSelectionText = dv;
13568         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13569         this.value = vv;
13570         
13571         
13572         
13573     },
13574     // private
13575     reset : function(){
13576         // overridden so that last data is reset..
13577         
13578         if(this.multiple){
13579             this.clearItem();
13580             return;
13581         }
13582         
13583         this.setValue(this.originalValue);
13584         //this.clearInvalid();
13585         this.lastData = false;
13586         if (this.view) {
13587             this.view.clearSelections();
13588         }
13589         
13590         this.validate();
13591     },
13592     // private
13593     findRecord : function(prop, value){
13594         var record;
13595         if(this.store.getCount() > 0){
13596             this.store.each(function(r){
13597                 if(r.data[prop] == value){
13598                     record = r;
13599                     return false;
13600                 }
13601                 return true;
13602             });
13603         }
13604         return record;
13605     },
13606     
13607     getName: function()
13608     {
13609         // returns hidden if it's set..
13610         if (!this.rendered) {return ''};
13611         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13612         
13613     },
13614     // private
13615     onViewMove : function(e, t){
13616         this.inKeyMode = false;
13617     },
13618
13619     // private
13620     onViewOver : function(e, t){
13621         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13622             return;
13623         }
13624         var item = this.view.findItemFromChild(t);
13625         
13626         if(item){
13627             var index = this.view.indexOf(item);
13628             this.select(index, false);
13629         }
13630     },
13631
13632     // private
13633     onViewClick : function(view, doFocus, el, e)
13634     {
13635         var index = this.view.getSelectedIndexes()[0];
13636         
13637         var r = this.store.getAt(index);
13638         
13639         if(this.tickable){
13640             
13641             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13642                 return;
13643             }
13644             
13645             var rm = false;
13646             var _this = this;
13647             
13648             Roo.each(this.tickItems, function(v,k){
13649                 
13650                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13651                     Roo.log(v);
13652                     _this.tickItems.splice(k, 1);
13653                     
13654                     if(typeof(e) == 'undefined' && view == false){
13655                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13656                     }
13657                     
13658                     rm = true;
13659                     return;
13660                 }
13661             });
13662             
13663             if(rm){
13664                 return;
13665             }
13666             
13667             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13668                 this.tickItems.push(r.data);
13669             }
13670             
13671             if(typeof(e) == 'undefined' && view == false){
13672                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13673             }
13674                     
13675             return;
13676         }
13677         
13678         if(r){
13679             this.onSelect(r, index);
13680         }
13681         if(doFocus !== false && !this.blockFocus){
13682             this.inputEl().focus();
13683         }
13684     },
13685
13686     // private
13687     restrictHeight : function(){
13688         //this.innerList.dom.style.height = '';
13689         //var inner = this.innerList.dom;
13690         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13691         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13692         //this.list.beginUpdate();
13693         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13694         this.list.alignTo(this.inputEl(), this.listAlign);
13695         this.list.alignTo(this.inputEl(), this.listAlign);
13696         //this.list.endUpdate();
13697     },
13698
13699     // private
13700     onEmptyResults : function(){
13701         
13702         if(this.tickable && this.editable){
13703             this.restrictHeight();
13704             return;
13705         }
13706         
13707         this.collapse();
13708     },
13709
13710     /**
13711      * Returns true if the dropdown list is expanded, else false.
13712      */
13713     isExpanded : function(){
13714         return this.list.isVisible();
13715     },
13716
13717     /**
13718      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13719      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13720      * @param {String} value The data value of the item to select
13721      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13722      * selected item if it is not currently in view (defaults to true)
13723      * @return {Boolean} True if the value matched an item in the list, else false
13724      */
13725     selectByValue : function(v, scrollIntoView){
13726         if(v !== undefined && v !== null){
13727             var r = this.findRecord(this.valueField || this.displayField, v);
13728             if(r){
13729                 this.select(this.store.indexOf(r), scrollIntoView);
13730                 return true;
13731             }
13732         }
13733         return false;
13734     },
13735
13736     /**
13737      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13738      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13739      * @param {Number} index The zero-based index of the list item to select
13740      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13741      * selected item if it is not currently in view (defaults to true)
13742      */
13743     select : function(index, scrollIntoView){
13744         this.selectedIndex = index;
13745         this.view.select(index);
13746         if(scrollIntoView !== false){
13747             var el = this.view.getNode(index);
13748             /*
13749              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13750              */
13751             if(el){
13752                 this.list.scrollChildIntoView(el, false);
13753             }
13754         }
13755     },
13756
13757     // private
13758     selectNext : function(){
13759         var ct = this.store.getCount();
13760         if(ct > 0){
13761             if(this.selectedIndex == -1){
13762                 this.select(0);
13763             }else if(this.selectedIndex < ct-1){
13764                 this.select(this.selectedIndex+1);
13765             }
13766         }
13767     },
13768
13769     // private
13770     selectPrev : function(){
13771         var ct = this.store.getCount();
13772         if(ct > 0){
13773             if(this.selectedIndex == -1){
13774                 this.select(0);
13775             }else if(this.selectedIndex != 0){
13776                 this.select(this.selectedIndex-1);
13777             }
13778         }
13779     },
13780
13781     // private
13782     onKeyUp : function(e){
13783         if(this.editable !== false && !e.isSpecialKey()){
13784             this.lastKey = e.getKey();
13785             this.dqTask.delay(this.queryDelay);
13786         }
13787     },
13788
13789     // private
13790     validateBlur : function(){
13791         return !this.list || !this.list.isVisible();   
13792     },
13793
13794     // private
13795     initQuery : function(){
13796         
13797         var v = this.getRawValue();
13798         
13799         if(this.tickable && this.editable){
13800             v = this.tickableInputEl().getValue();
13801         }
13802         
13803         this.doQuery(v);
13804     },
13805
13806     // private
13807     doForce : function(){
13808         if(this.inputEl().dom.value.length > 0){
13809             this.inputEl().dom.value =
13810                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13811              
13812         }
13813     },
13814
13815     /**
13816      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13817      * query allowing the query action to be canceled if needed.
13818      * @param {String} query The SQL query to execute
13819      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13820      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13821      * saved in the current store (defaults to false)
13822      */
13823     doQuery : function(q, forceAll){
13824         
13825         if(q === undefined || q === null){
13826             q = '';
13827         }
13828         var qe = {
13829             query: q,
13830             forceAll: forceAll,
13831             combo: this,
13832             cancel:false
13833         };
13834         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13835             return false;
13836         }
13837         q = qe.query;
13838         
13839         forceAll = qe.forceAll;
13840         if(forceAll === true || (q.length >= this.minChars)){
13841             
13842             this.hasQuery = true;
13843             
13844             if(this.lastQuery != q || this.alwaysQuery){
13845                 this.lastQuery = q;
13846                 if(this.mode == 'local'){
13847                     this.selectedIndex = -1;
13848                     if(forceAll){
13849                         this.store.clearFilter();
13850                     }else{
13851                         
13852                         if(this.specialFilter){
13853                             this.fireEvent('specialfilter', this);
13854                             this.onLoad();
13855                             return;
13856                         }
13857                         
13858                         this.store.filter(this.displayField, q);
13859                     }
13860                     
13861                     this.store.fireEvent("datachanged", this.store);
13862                     
13863                     this.onLoad();
13864                     
13865                     
13866                 }else{
13867                     
13868                     this.store.baseParams[this.queryParam] = q;
13869                     
13870                     var options = {params : this.getParams(q)};
13871                     
13872                     if(this.loadNext){
13873                         options.add = true;
13874                         options.params.start = this.page * this.pageSize;
13875                     }
13876                     
13877                     this.store.load(options);
13878                     
13879                     /*
13880                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13881                      *  we should expand the list on onLoad
13882                      *  so command out it
13883                      */
13884 //                    this.expand();
13885                 }
13886             }else{
13887                 this.selectedIndex = -1;
13888                 this.onLoad();   
13889             }
13890         }
13891         
13892         this.loadNext = false;
13893     },
13894     
13895     // private
13896     getParams : function(q){
13897         var p = {};
13898         //p[this.queryParam] = q;
13899         
13900         if(this.pageSize){
13901             p.start = 0;
13902             p.limit = this.pageSize;
13903         }
13904         return p;
13905     },
13906
13907     /**
13908      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13909      */
13910     collapse : function(){
13911         if(!this.isExpanded()){
13912             return;
13913         }
13914         
13915         this.list.hide();
13916         
13917         this.hasFocus = false;
13918         
13919         if(this.tickable){
13920             this.okBtn.hide();
13921             this.cancelBtn.hide();
13922             this.trigger.show();
13923             
13924             if(this.editable){
13925                 this.tickableInputEl().dom.value = '';
13926                 this.tickableInputEl().blur();
13927             }
13928             
13929         }
13930         
13931         Roo.get(document).un('mousedown', this.collapseIf, this);
13932         Roo.get(document).un('mousewheel', this.collapseIf, this);
13933         if (!this.editable) {
13934             Roo.get(document).un('keydown', this.listKeyPress, this);
13935         }
13936         this.fireEvent('collapse', this);
13937         
13938         this.validate();
13939     },
13940
13941     // private
13942     collapseIf : function(e){
13943         var in_combo  = e.within(this.el);
13944         var in_list =  e.within(this.list);
13945         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13946         
13947         if (in_combo || in_list || is_list) {
13948             //e.stopPropagation();
13949             return;
13950         }
13951         
13952         if(this.tickable){
13953             this.onTickableFooterButtonClick(e, false, false);
13954         }
13955
13956         this.collapse();
13957         
13958     },
13959
13960     /**
13961      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13962      */
13963     expand : function(){
13964        
13965         if(this.isExpanded() || !this.hasFocus){
13966             return;
13967         }
13968         
13969         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13970         this.list.setWidth(lw);
13971         
13972         Roo.log('expand');
13973         
13974         this.list.show();
13975         
13976         this.restrictHeight();
13977         
13978         if(this.tickable){
13979             
13980             this.tickItems = Roo.apply([], this.item);
13981             
13982             this.okBtn.show();
13983             this.cancelBtn.show();
13984             this.trigger.hide();
13985             
13986             if(this.editable){
13987                 this.tickableInputEl().focus();
13988             }
13989             
13990         }
13991         
13992         Roo.get(document).on('mousedown', this.collapseIf, this);
13993         Roo.get(document).on('mousewheel', this.collapseIf, this);
13994         if (!this.editable) {
13995             Roo.get(document).on('keydown', this.listKeyPress, this);
13996         }
13997         
13998         this.fireEvent('expand', this);
13999     },
14000
14001     // private
14002     // Implements the default empty TriggerField.onTriggerClick function
14003     onTriggerClick : function(e)
14004     {
14005         Roo.log('trigger click');
14006         
14007         if(this.disabled || !this.triggerList){
14008             return;
14009         }
14010         
14011         this.page = 0;
14012         this.loadNext = false;
14013         
14014         if(this.isExpanded()){
14015             this.collapse();
14016             if (!this.blockFocus) {
14017                 this.inputEl().focus();
14018             }
14019             
14020         }else {
14021             this.hasFocus = true;
14022             if(this.triggerAction == 'all') {
14023                 this.doQuery(this.allQuery, true);
14024             } else {
14025                 this.doQuery(this.getRawValue());
14026             }
14027             if (!this.blockFocus) {
14028                 this.inputEl().focus();
14029             }
14030         }
14031     },
14032     
14033     onTickableTriggerClick : function(e)
14034     {
14035         if(this.disabled){
14036             return;
14037         }
14038         
14039         this.page = 0;
14040         this.loadNext = false;
14041         this.hasFocus = true;
14042         
14043         if(this.triggerAction == 'all') {
14044             this.doQuery(this.allQuery, true);
14045         } else {
14046             this.doQuery(this.getRawValue());
14047         }
14048     },
14049     
14050     onSearchFieldClick : function(e)
14051     {
14052         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14053             this.onTickableFooterButtonClick(e, false, false);
14054             return;
14055         }
14056         
14057         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14058             return;
14059         }
14060         
14061         this.page = 0;
14062         this.loadNext = false;
14063         this.hasFocus = true;
14064         
14065         if(this.triggerAction == 'all') {
14066             this.doQuery(this.allQuery, true);
14067         } else {
14068             this.doQuery(this.getRawValue());
14069         }
14070     },
14071     
14072     listKeyPress : function(e)
14073     {
14074         //Roo.log('listkeypress');
14075         // scroll to first matching element based on key pres..
14076         if (e.isSpecialKey()) {
14077             return false;
14078         }
14079         var k = String.fromCharCode(e.getKey()).toUpperCase();
14080         //Roo.log(k);
14081         var match  = false;
14082         var csel = this.view.getSelectedNodes();
14083         var cselitem = false;
14084         if (csel.length) {
14085             var ix = this.view.indexOf(csel[0]);
14086             cselitem  = this.store.getAt(ix);
14087             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14088                 cselitem = false;
14089             }
14090             
14091         }
14092         
14093         this.store.each(function(v) { 
14094             if (cselitem) {
14095                 // start at existing selection.
14096                 if (cselitem.id == v.id) {
14097                     cselitem = false;
14098                 }
14099                 return true;
14100             }
14101                 
14102             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14103                 match = this.store.indexOf(v);
14104                 return false;
14105             }
14106             return true;
14107         }, this);
14108         
14109         if (match === false) {
14110             return true; // no more action?
14111         }
14112         // scroll to?
14113         this.view.select(match);
14114         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14115         sn.scrollIntoView(sn.dom.parentNode, false);
14116     },
14117     
14118     onViewScroll : function(e, t){
14119         
14120         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){
14121             return;
14122         }
14123         
14124         this.hasQuery = true;
14125         
14126         this.loading = this.list.select('.loading', true).first();
14127         
14128         if(this.loading === null){
14129             this.list.createChild({
14130                 tag: 'div',
14131                 cls: 'loading roo-select2-more-results roo-select2-active',
14132                 html: 'Loading more results...'
14133             });
14134             
14135             this.loading = this.list.select('.loading', true).first();
14136             
14137             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14138             
14139             this.loading.hide();
14140         }
14141         
14142         this.loading.show();
14143         
14144         var _combo = this;
14145         
14146         this.page++;
14147         this.loadNext = true;
14148         
14149         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14150         
14151         return;
14152     },
14153     
14154     addItem : function(o)
14155     {   
14156         var dv = ''; // display value
14157         
14158         if (this.displayField) {
14159             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14160         } else {
14161             // this is an error condition!!!
14162             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14163         }
14164         
14165         if(!dv.length){
14166             return;
14167         }
14168         
14169         var choice = this.choices.createChild({
14170             tag: 'li',
14171             cls: 'roo-select2-search-choice',
14172             cn: [
14173                 {
14174                     tag: 'div',
14175                     html: dv
14176                 },
14177                 {
14178                     tag: 'a',
14179                     href: '#',
14180                     cls: 'roo-select2-search-choice-close',
14181                     tabindex: '-1'
14182                 }
14183             ]
14184             
14185         }, this.searchField);
14186         
14187         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14188         
14189         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14190         
14191         this.item.push(o);
14192         
14193         this.lastData = o;
14194         
14195         this.syncValue();
14196         
14197         this.inputEl().dom.value = '';
14198         
14199         this.validate();
14200     },
14201     
14202     onRemoveItem : function(e, _self, o)
14203     {
14204         e.preventDefault();
14205         
14206         this.lastItem = Roo.apply([], this.item);
14207         
14208         var index = this.item.indexOf(o.data) * 1;
14209         
14210         if( index < 0){
14211             Roo.log('not this item?!');
14212             return;
14213         }
14214         
14215         this.item.splice(index, 1);
14216         o.item.remove();
14217         
14218         this.syncValue();
14219         
14220         this.fireEvent('remove', this, e);
14221         
14222         this.validate();
14223         
14224     },
14225     
14226     syncValue : function()
14227     {
14228         if(!this.item.length){
14229             this.clearValue();
14230             return;
14231         }
14232             
14233         var value = [];
14234         var _this = this;
14235         Roo.each(this.item, function(i){
14236             if(_this.valueField){
14237                 value.push(i[_this.valueField]);
14238                 return;
14239             }
14240
14241             value.push(i);
14242         });
14243
14244         this.value = value.join(',');
14245
14246         if(this.hiddenField){
14247             this.hiddenField.dom.value = this.value;
14248         }
14249         
14250         this.store.fireEvent("datachanged", this.store);
14251         
14252         this.validate();
14253     },
14254     
14255     clearItem : function()
14256     {
14257         if(!this.multiple){
14258             return;
14259         }
14260         
14261         this.item = [];
14262         
14263         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14264            c.remove();
14265         });
14266         
14267         this.syncValue();
14268         
14269         this.validate();
14270         
14271         if(this.tickable && !Roo.isTouch){
14272             this.view.refresh();
14273         }
14274     },
14275     
14276     inputEl: function ()
14277     {
14278         if(Roo.isIOS && this.useNativeIOS){
14279             return this.el.select('select.roo-ios-select', true).first();
14280         }
14281         
14282         if(Roo.isTouch && this.mobileTouchView){
14283             return this.el.select('input.form-control',true).first();
14284         }
14285         
14286         if(this.tickable){
14287             return this.searchField;
14288         }
14289         
14290         return this.el.select('input.form-control',true).first();
14291     },
14292     
14293     onTickableFooterButtonClick : function(e, btn, el)
14294     {
14295         e.preventDefault();
14296         
14297         this.lastItem = Roo.apply([], this.item);
14298         
14299         if(btn && btn.name == 'cancel'){
14300             this.tickItems = Roo.apply([], this.item);
14301             this.collapse();
14302             return;
14303         }
14304         
14305         this.clearItem();
14306         
14307         var _this = this;
14308         
14309         Roo.each(this.tickItems, function(o){
14310             _this.addItem(o);
14311         });
14312         
14313         this.collapse();
14314         
14315     },
14316     
14317     validate : function()
14318     {
14319         var v = this.getRawValue();
14320         
14321         if(this.multiple){
14322             v = this.getValue();
14323         }
14324         
14325         if(this.disabled || this.allowBlank || v.length){
14326             this.markValid();
14327             return true;
14328         }
14329         
14330         this.markInvalid();
14331         return false;
14332     },
14333     
14334     tickableInputEl : function()
14335     {
14336         if(!this.tickable || !this.editable){
14337             return this.inputEl();
14338         }
14339         
14340         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14341     },
14342     
14343     
14344     getAutoCreateTouchView : function()
14345     {
14346         var id = Roo.id();
14347         
14348         var cfg = {
14349             cls: 'form-group' //input-group
14350         };
14351         
14352         var input =  {
14353             tag: 'input',
14354             id : id,
14355             type : this.inputType,
14356             cls : 'form-control x-combo-noedit',
14357             autocomplete: 'new-password',
14358             placeholder : this.placeholder || '',
14359             readonly : true
14360         };
14361         
14362         if (this.name) {
14363             input.name = this.name;
14364         }
14365         
14366         if (this.size) {
14367             input.cls += ' input-' + this.size;
14368         }
14369         
14370         if (this.disabled) {
14371             input.disabled = true;
14372         }
14373         
14374         var inputblock = {
14375             cls : '',
14376             cn : [
14377                 input
14378             ]
14379         };
14380         
14381         if(this.before){
14382             inputblock.cls += ' input-group';
14383             
14384             inputblock.cn.unshift({
14385                 tag :'span',
14386                 cls : 'input-group-addon',
14387                 html : this.before
14388             });
14389         }
14390         
14391         if(this.removable && !this.multiple){
14392             inputblock.cls += ' roo-removable';
14393             
14394             inputblock.cn.push({
14395                 tag: 'button',
14396                 html : 'x',
14397                 cls : 'roo-combo-removable-btn close'
14398             });
14399         }
14400
14401         if(this.hasFeedback && !this.allowBlank){
14402             
14403             inputblock.cls += ' has-feedback';
14404             
14405             inputblock.cn.push({
14406                 tag: 'span',
14407                 cls: 'glyphicon form-control-feedback'
14408             });
14409             
14410         }
14411         
14412         if (this.after) {
14413             
14414             inputblock.cls += (this.before) ? '' : ' input-group';
14415             
14416             inputblock.cn.push({
14417                 tag :'span',
14418                 cls : 'input-group-addon',
14419                 html : this.after
14420             });
14421         }
14422
14423         var box = {
14424             tag: 'div',
14425             cn: [
14426                 {
14427                     tag: 'input',
14428                     type : 'hidden',
14429                     cls: 'form-hidden-field'
14430                 },
14431                 inputblock
14432             ]
14433             
14434         };
14435         
14436         if(this.multiple){
14437             box = {
14438                 tag: 'div',
14439                 cn: [
14440                     {
14441                         tag: 'input',
14442                         type : 'hidden',
14443                         cls: 'form-hidden-field'
14444                     },
14445                     {
14446                         tag: 'ul',
14447                         cls: 'roo-select2-choices',
14448                         cn:[
14449                             {
14450                                 tag: 'li',
14451                                 cls: 'roo-select2-search-field',
14452                                 cn: [
14453
14454                                     inputblock
14455                                 ]
14456                             }
14457                         ]
14458                     }
14459                 ]
14460             }
14461         };
14462         
14463         var combobox = {
14464             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14465             cn: [
14466                 box
14467             ]
14468         };
14469         
14470         if(!this.multiple && this.showToggleBtn){
14471             
14472             var caret = {
14473                         tag: 'span',
14474                         cls: 'caret'
14475             };
14476             
14477             if (this.caret != false) {
14478                 caret = {
14479                      tag: 'i',
14480                      cls: 'fa fa-' + this.caret
14481                 };
14482                 
14483             }
14484             
14485             combobox.cn.push({
14486                 tag :'span',
14487                 cls : 'input-group-addon btn dropdown-toggle',
14488                 cn : [
14489                     caret,
14490                     {
14491                         tag: 'span',
14492                         cls: 'combobox-clear',
14493                         cn  : [
14494                             {
14495                                 tag : 'i',
14496                                 cls: 'icon-remove'
14497                             }
14498                         ]
14499                     }
14500                 ]
14501
14502             })
14503         }
14504         
14505         if(this.multiple){
14506             combobox.cls += ' roo-select2-container-multi';
14507         }
14508         
14509         var align = this.labelAlign || this.parentLabelAlign();
14510         
14511         if (align ==='left' && this.fieldLabel.length) {
14512             
14513             cfg.cn = [
14514                 {
14515                    tag : 'i',
14516                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14517                    tooltip : 'This field is required'
14518                 },
14519                 {
14520                     tag: 'label',
14521                     cls : 'control-label',
14522                     html : this.fieldLabel
14523
14524                 },
14525                 {
14526                     cls : '', 
14527                     cn: [
14528                         combobox
14529                     ]
14530                 }
14531             ];
14532             
14533             var labelCfg = cfg.cn[1];
14534             var contentCfg = cfg.cn[2];
14535             
14536
14537             if(this.indicatorpos == 'right'){
14538                 cfg.cn = [
14539                     {
14540                         tag: 'label',
14541                         cls : 'control-label',
14542                         html : this.fieldLabel
14543
14544                     },
14545                     {
14546                        tag : 'i',
14547                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14548                        tooltip : 'This field is required'
14549                     },
14550                     {
14551                         cls : '', 
14552                         cn: [
14553                             combobox
14554                         ]
14555                     }
14556                 ];
14557             }
14558             
14559             labelCfg = cfg.cn[0];
14560             
14561             if(this.labelWidth > 12){
14562                 labelCfg.style = "width: " + this.labelWidth + 'px';
14563             }
14564             
14565             if(this.labelWidth < 13 && this.labelmd == 0){
14566                 this.labelmd = this.labelWidth;
14567             }
14568             
14569             if(this.labellg > 0){
14570                 labelCfg.cls += ' col-lg-' + this.labellg;
14571                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14572             }
14573             
14574             if(this.labelmd > 0){
14575                 labelCfg.cls += ' col-md-' + this.labelmd;
14576                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14577             }
14578             
14579             if(this.labelsm > 0){
14580                 labelCfg.cls += ' col-sm-' + this.labelsm;
14581                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14582             }
14583             
14584             if(this.labelxs > 0){
14585                 labelCfg.cls += ' col-xs-' + this.labelxs;
14586                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14587             }
14588                 
14589                 
14590         } else if ( this.fieldLabel.length) {
14591             cfg.cn = [
14592                 {
14593                    tag : 'i',
14594                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14595                    tooltip : 'This field is required'
14596                 },
14597                 {
14598                     tag: 'label',
14599                     cls : 'control-label',
14600                     html : this.fieldLabel
14601
14602                 },
14603                 {
14604                     cls : '', 
14605                     cn: [
14606                         combobox
14607                     ]
14608                 }
14609             ];
14610             
14611             if(this.indicatorpos == 'right'){
14612                 cfg.cn = [
14613                     {
14614                         tag: 'label',
14615                         cls : 'control-label',
14616                         html : this.fieldLabel
14617
14618                     },
14619                     {
14620                        tag : 'i',
14621                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14622                        tooltip : 'This field is required'
14623                     },
14624                     {
14625                         cls : '', 
14626                         cn: [
14627                             combobox
14628                         ]
14629                     }
14630                 ];
14631             }
14632         } else {
14633             cfg.cn = combobox;    
14634         }
14635         
14636         
14637         var settings = this;
14638         
14639         ['xs','sm','md','lg'].map(function(size){
14640             if (settings[size]) {
14641                 cfg.cls += ' col-' + size + '-' + settings[size];
14642             }
14643         });
14644         
14645         return cfg;
14646     },
14647     
14648     initTouchView : function()
14649     {
14650         this.renderTouchView();
14651         
14652         this.touchViewEl.on('scroll', function(){
14653             this.el.dom.scrollTop = 0;
14654         }, this);
14655         
14656         this.originalValue = this.getValue();
14657         
14658         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14659         
14660         this.inputEl().on("click", this.showTouchView, this);
14661         if (this.triggerEl) {
14662             this.triggerEl.on("click", this.showTouchView, this);
14663         }
14664         
14665         
14666         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14667         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14668         
14669         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14670         
14671         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14672         this.store.on('load', this.onTouchViewLoad, this);
14673         this.store.on('loadexception', this.onTouchViewLoadException, this);
14674         
14675         if(this.hiddenName){
14676             
14677             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14678             
14679             this.hiddenField.dom.value =
14680                 this.hiddenValue !== undefined ? this.hiddenValue :
14681                 this.value !== undefined ? this.value : '';
14682         
14683             this.el.dom.removeAttribute('name');
14684             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14685         }
14686         
14687         if(this.multiple){
14688             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14689             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14690         }
14691         
14692         if(this.removable && !this.multiple){
14693             var close = this.closeTriggerEl();
14694             if(close){
14695                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14696                 close.on('click', this.removeBtnClick, this, close);
14697             }
14698         }
14699         /*
14700          * fix the bug in Safari iOS8
14701          */
14702         this.inputEl().on("focus", function(e){
14703             document.activeElement.blur();
14704         }, this);
14705         
14706         return;
14707         
14708         
14709     },
14710     
14711     renderTouchView : function()
14712     {
14713         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14714         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14715         
14716         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14717         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14718         
14719         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14720         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14721         this.touchViewBodyEl.setStyle('overflow', 'auto');
14722         
14723         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14724         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14725         
14726         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14727         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14728         
14729     },
14730     
14731     showTouchView : function()
14732     {
14733         if(this.disabled){
14734             return;
14735         }
14736         
14737         this.touchViewHeaderEl.hide();
14738
14739         if(this.modalTitle.length){
14740             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14741             this.touchViewHeaderEl.show();
14742         }
14743
14744         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14745         this.touchViewEl.show();
14746
14747         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14748         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14749                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14750
14751         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14752
14753         if(this.modalTitle.length){
14754             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14755         }
14756         
14757         this.touchViewBodyEl.setHeight(bodyHeight);
14758
14759         if(this.animate){
14760             var _this = this;
14761             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14762         }else{
14763             this.touchViewEl.addClass('in');
14764         }
14765
14766         this.doTouchViewQuery();
14767         
14768     },
14769     
14770     hideTouchView : function()
14771     {
14772         this.touchViewEl.removeClass('in');
14773
14774         if(this.animate){
14775             var _this = this;
14776             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14777         }else{
14778             this.touchViewEl.setStyle('display', 'none');
14779         }
14780         
14781     },
14782     
14783     setTouchViewValue : function()
14784     {
14785         if(this.multiple){
14786             this.clearItem();
14787         
14788             var _this = this;
14789
14790             Roo.each(this.tickItems, function(o){
14791                 this.addItem(o);
14792             }, this);
14793         }
14794         
14795         this.hideTouchView();
14796     },
14797     
14798     doTouchViewQuery : function()
14799     {
14800         var qe = {
14801             query: '',
14802             forceAll: true,
14803             combo: this,
14804             cancel:false
14805         };
14806         
14807         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14808             return false;
14809         }
14810         
14811         if(!this.alwaysQuery || this.mode == 'local'){
14812             this.onTouchViewLoad();
14813             return;
14814         }
14815         
14816         this.store.load();
14817     },
14818     
14819     onTouchViewBeforeLoad : function(combo,opts)
14820     {
14821         return;
14822     },
14823
14824     // private
14825     onTouchViewLoad : function()
14826     {
14827         if(this.store.getCount() < 1){
14828             this.onTouchViewEmptyResults();
14829             return;
14830         }
14831         
14832         this.clearTouchView();
14833         
14834         var rawValue = this.getRawValue();
14835         
14836         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14837         
14838         this.tickItems = [];
14839         
14840         this.store.data.each(function(d, rowIndex){
14841             var row = this.touchViewListGroup.createChild(template);
14842             
14843             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14844                 row.addClass(d.data.cls);
14845             }
14846             
14847             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14848                 var cfg = {
14849                     data : d.data,
14850                     html : d.data[this.displayField]
14851                 };
14852                 
14853                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14854                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14855                 }
14856             }
14857             row.removeClass('selected');
14858             if(!this.multiple && this.valueField &&
14859                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14860             {
14861                 // radio buttons..
14862                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14863                 row.addClass('selected');
14864             }
14865             
14866             if(this.multiple && this.valueField &&
14867                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14868             {
14869                 
14870                 // checkboxes...
14871                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14872                 this.tickItems.push(d.data);
14873             }
14874             
14875             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14876             
14877         }, this);
14878         
14879         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14880         
14881         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14882
14883         if(this.modalTitle.length){
14884             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14885         }
14886
14887         var listHeight = this.touchViewListGroup.getHeight();
14888         
14889         var _this = this;
14890         
14891         if(firstChecked && listHeight > bodyHeight){
14892             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14893         }
14894         
14895     },
14896     
14897     onTouchViewLoadException : function()
14898     {
14899         this.hideTouchView();
14900     },
14901     
14902     onTouchViewEmptyResults : function()
14903     {
14904         this.clearTouchView();
14905         
14906         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14907         
14908         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14909         
14910     },
14911     
14912     clearTouchView : function()
14913     {
14914         this.touchViewListGroup.dom.innerHTML = '';
14915     },
14916     
14917     onTouchViewClick : function(e, el, o)
14918     {
14919         e.preventDefault();
14920         
14921         var row = o.row;
14922         var rowIndex = o.rowIndex;
14923         
14924         var r = this.store.getAt(rowIndex);
14925         
14926         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14927             
14928             if(!this.multiple){
14929                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14930                     c.dom.removeAttribute('checked');
14931                 }, this);
14932
14933                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14934
14935                 this.setFromData(r.data);
14936
14937                 var close = this.closeTriggerEl();
14938
14939                 if(close){
14940                     close.show();
14941                 }
14942
14943                 this.hideTouchView();
14944
14945                 this.fireEvent('select', this, r, rowIndex);
14946
14947                 return;
14948             }
14949
14950             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14951                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14952                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14953                 return;
14954             }
14955
14956             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14957             this.addItem(r.data);
14958             this.tickItems.push(r.data);
14959         }
14960     },
14961     
14962     getAutoCreateNativeIOS : function()
14963     {
14964         var cfg = {
14965             cls: 'form-group' //input-group,
14966         };
14967         
14968         var combobox =  {
14969             tag: 'select',
14970             cls : 'roo-ios-select'
14971         };
14972         
14973         if (this.name) {
14974             combobox.name = this.name;
14975         }
14976         
14977         if (this.disabled) {
14978             combobox.disabled = true;
14979         }
14980         
14981         var settings = this;
14982         
14983         ['xs','sm','md','lg'].map(function(size){
14984             if (settings[size]) {
14985                 cfg.cls += ' col-' + size + '-' + settings[size];
14986             }
14987         });
14988         
14989         cfg.cn = combobox;
14990         
14991         return cfg;
14992         
14993     },
14994     
14995     initIOSView : function()
14996     {
14997         this.store.on('load', this.onIOSViewLoad, this);
14998         
14999         return;
15000     },
15001     
15002     onIOSViewLoad : function()
15003     {
15004         if(this.store.getCount() < 1){
15005             return;
15006         }
15007         
15008         this.clearIOSView();
15009         
15010         if(this.allowBlank) {
15011             
15012             var default_text = '-- SELECT --';
15013             
15014             var opt = this.inputEl().createChild({
15015                 tag: 'option',
15016                 value : 0,
15017                 html : default_text
15018             });
15019             
15020             var o = {};
15021             o[this.valueField] = 0;
15022             o[this.displayField] = default_text;
15023             
15024             this.ios_options.push({
15025                 data : o,
15026                 el : opt
15027             });
15028             
15029         }
15030         
15031         this.store.data.each(function(d, rowIndex){
15032             
15033             var html = '';
15034             
15035             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15036                 html = d.data[this.displayField];
15037             }
15038             
15039             var value = '';
15040             
15041             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15042                 value = d.data[this.valueField];
15043             }
15044             
15045             var option = {
15046                 tag: 'option',
15047                 value : value,
15048                 html : html
15049             };
15050             
15051             if(this.value == d.data[this.valueField]){
15052                 option['selected'] = true;
15053             }
15054             
15055             var opt = this.inputEl().createChild(option);
15056             
15057             this.ios_options.push({
15058                 data : d.data,
15059                 el : opt
15060             });
15061             
15062         }, this);
15063         
15064         this.inputEl().on('change', function(){
15065            this.fireEvent('select', this);
15066         }, this);
15067         
15068     },
15069     
15070     clearIOSView: function()
15071     {
15072         this.inputEl().dom.innerHTML = '';
15073         
15074         this.ios_options = [];
15075     },
15076     
15077     setIOSValue: function(v)
15078     {
15079         this.value = v;
15080         
15081         if(!this.ios_options){
15082             return;
15083         }
15084         
15085         Roo.each(this.ios_options, function(opts){
15086            
15087            opts.el.dom.removeAttribute('selected');
15088            
15089            if(opts.data[this.valueField] != v){
15090                return;
15091            }
15092            
15093            opts.el.dom.setAttribute('selected', true);
15094            
15095         }, this);
15096     }
15097
15098     /** 
15099     * @cfg {Boolean} grow 
15100     * @hide 
15101     */
15102     /** 
15103     * @cfg {Number} growMin 
15104     * @hide 
15105     */
15106     /** 
15107     * @cfg {Number} growMax 
15108     * @hide 
15109     */
15110     /**
15111      * @hide
15112      * @method autoSize
15113      */
15114 });
15115
15116 Roo.apply(Roo.bootstrap.ComboBox,  {
15117     
15118     header : {
15119         tag: 'div',
15120         cls: 'modal-header',
15121         cn: [
15122             {
15123                 tag: 'h4',
15124                 cls: 'modal-title'
15125             }
15126         ]
15127     },
15128     
15129     body : {
15130         tag: 'div',
15131         cls: 'modal-body',
15132         cn: [
15133             {
15134                 tag: 'ul',
15135                 cls: 'list-group'
15136             }
15137         ]
15138     },
15139     
15140     listItemRadio : {
15141         tag: 'li',
15142         cls: 'list-group-item',
15143         cn: [
15144             {
15145                 tag: 'span',
15146                 cls: 'roo-combobox-list-group-item-value'
15147             },
15148             {
15149                 tag: 'div',
15150                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15151                 cn: [
15152                     {
15153                         tag: 'input',
15154                         type: 'radio'
15155                     },
15156                     {
15157                         tag: 'label'
15158                     }
15159                 ]
15160             }
15161         ]
15162     },
15163     
15164     listItemCheckbox : {
15165         tag: 'li',
15166         cls: 'list-group-item',
15167         cn: [
15168             {
15169                 tag: 'span',
15170                 cls: 'roo-combobox-list-group-item-value'
15171             },
15172             {
15173                 tag: 'div',
15174                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15175                 cn: [
15176                     {
15177                         tag: 'input',
15178                         type: 'checkbox'
15179                     },
15180                     {
15181                         tag: 'label'
15182                     }
15183                 ]
15184             }
15185         ]
15186     },
15187     
15188     emptyResult : {
15189         tag: 'div',
15190         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15191     },
15192     
15193     footer : {
15194         tag: 'div',
15195         cls: 'modal-footer',
15196         cn: [
15197             {
15198                 tag: 'div',
15199                 cls: 'row',
15200                 cn: [
15201                     {
15202                         tag: 'div',
15203                         cls: 'col-xs-6 text-left',
15204                         cn: {
15205                             tag: 'button',
15206                             cls: 'btn btn-danger roo-touch-view-cancel',
15207                             html: 'Cancel'
15208                         }
15209                     },
15210                     {
15211                         tag: 'div',
15212                         cls: 'col-xs-6 text-right',
15213                         cn: {
15214                             tag: 'button',
15215                             cls: 'btn btn-success roo-touch-view-ok',
15216                             html: 'OK'
15217                         }
15218                     }
15219                 ]
15220             }
15221         ]
15222         
15223     }
15224 });
15225
15226 Roo.apply(Roo.bootstrap.ComboBox,  {
15227     
15228     touchViewTemplate : {
15229         tag: 'div',
15230         cls: 'modal fade roo-combobox-touch-view',
15231         cn: [
15232             {
15233                 tag: 'div',
15234                 cls: 'modal-dialog',
15235                 style : 'position:fixed', // we have to fix position....
15236                 cn: [
15237                     {
15238                         tag: 'div',
15239                         cls: 'modal-content',
15240                         cn: [
15241                             Roo.bootstrap.ComboBox.header,
15242                             Roo.bootstrap.ComboBox.body,
15243                             Roo.bootstrap.ComboBox.footer
15244                         ]
15245                     }
15246                 ]
15247             }
15248         ]
15249     }
15250 });/*
15251  * Based on:
15252  * Ext JS Library 1.1.1
15253  * Copyright(c) 2006-2007, Ext JS, LLC.
15254  *
15255  * Originally Released Under LGPL - original licence link has changed is not relivant.
15256  *
15257  * Fork - LGPL
15258  * <script type="text/javascript">
15259  */
15260
15261 /**
15262  * @class Roo.View
15263  * @extends Roo.util.Observable
15264  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15265  * This class also supports single and multi selection modes. <br>
15266  * Create a data model bound view:
15267  <pre><code>
15268  var store = new Roo.data.Store(...);
15269
15270  var view = new Roo.View({
15271     el : "my-element",
15272     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15273  
15274     singleSelect: true,
15275     selectedClass: "ydataview-selected",
15276     store: store
15277  });
15278
15279  // listen for node click?
15280  view.on("click", function(vw, index, node, e){
15281  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15282  });
15283
15284  // load XML data
15285  dataModel.load("foobar.xml");
15286  </code></pre>
15287  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15288  * <br><br>
15289  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15290  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15291  * 
15292  * Note: old style constructor is still suported (container, template, config)
15293  * 
15294  * @constructor
15295  * Create a new View
15296  * @param {Object} config The config object
15297  * 
15298  */
15299 Roo.View = function(config, depreciated_tpl, depreciated_config){
15300     
15301     this.parent = false;
15302     
15303     if (typeof(depreciated_tpl) == 'undefined') {
15304         // new way.. - universal constructor.
15305         Roo.apply(this, config);
15306         this.el  = Roo.get(this.el);
15307     } else {
15308         // old format..
15309         this.el  = Roo.get(config);
15310         this.tpl = depreciated_tpl;
15311         Roo.apply(this, depreciated_config);
15312     }
15313     this.wrapEl  = this.el.wrap().wrap();
15314     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15315     
15316     
15317     if(typeof(this.tpl) == "string"){
15318         this.tpl = new Roo.Template(this.tpl);
15319     } else {
15320         // support xtype ctors..
15321         this.tpl = new Roo.factory(this.tpl, Roo);
15322     }
15323     
15324     
15325     this.tpl.compile();
15326     
15327     /** @private */
15328     this.addEvents({
15329         /**
15330          * @event beforeclick
15331          * Fires before a click is processed. Returns false to cancel the default action.
15332          * @param {Roo.View} this
15333          * @param {Number} index The index of the target node
15334          * @param {HTMLElement} node The target node
15335          * @param {Roo.EventObject} e The raw event object
15336          */
15337             "beforeclick" : true,
15338         /**
15339          * @event click
15340          * Fires when a template node is clicked.
15341          * @param {Roo.View} this
15342          * @param {Number} index The index of the target node
15343          * @param {HTMLElement} node The target node
15344          * @param {Roo.EventObject} e The raw event object
15345          */
15346             "click" : true,
15347         /**
15348          * @event dblclick
15349          * Fires when a template node is double clicked.
15350          * @param {Roo.View} this
15351          * @param {Number} index The index of the target node
15352          * @param {HTMLElement} node The target node
15353          * @param {Roo.EventObject} e The raw event object
15354          */
15355             "dblclick" : true,
15356         /**
15357          * @event contextmenu
15358          * Fires when a template node is right clicked.
15359          * @param {Roo.View} this
15360          * @param {Number} index The index of the target node
15361          * @param {HTMLElement} node The target node
15362          * @param {Roo.EventObject} e The raw event object
15363          */
15364             "contextmenu" : true,
15365         /**
15366          * @event selectionchange
15367          * Fires when the selected nodes change.
15368          * @param {Roo.View} this
15369          * @param {Array} selections Array of the selected nodes
15370          */
15371             "selectionchange" : true,
15372     
15373         /**
15374          * @event beforeselect
15375          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15376          * @param {Roo.View} this
15377          * @param {HTMLElement} node The node to be selected
15378          * @param {Array} selections Array of currently selected nodes
15379          */
15380             "beforeselect" : true,
15381         /**
15382          * @event preparedata
15383          * Fires on every row to render, to allow you to change the data.
15384          * @param {Roo.View} this
15385          * @param {Object} data to be rendered (change this)
15386          */
15387           "preparedata" : true
15388           
15389           
15390         });
15391
15392
15393
15394     this.el.on({
15395         "click": this.onClick,
15396         "dblclick": this.onDblClick,
15397         "contextmenu": this.onContextMenu,
15398         scope:this
15399     });
15400
15401     this.selections = [];
15402     this.nodes = [];
15403     this.cmp = new Roo.CompositeElementLite([]);
15404     if(this.store){
15405         this.store = Roo.factory(this.store, Roo.data);
15406         this.setStore(this.store, true);
15407     }
15408     
15409     if ( this.footer && this.footer.xtype) {
15410            
15411          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15412         
15413         this.footer.dataSource = this.store;
15414         this.footer.container = fctr;
15415         this.footer = Roo.factory(this.footer, Roo);
15416         fctr.insertFirst(this.el);
15417         
15418         // this is a bit insane - as the paging toolbar seems to detach the el..
15419 //        dom.parentNode.parentNode.parentNode
15420          // they get detached?
15421     }
15422     
15423     
15424     Roo.View.superclass.constructor.call(this);
15425     
15426     
15427 };
15428
15429 Roo.extend(Roo.View, Roo.util.Observable, {
15430     
15431      /**
15432      * @cfg {Roo.data.Store} store Data store to load data from.
15433      */
15434     store : false,
15435     
15436     /**
15437      * @cfg {String|Roo.Element} el The container element.
15438      */
15439     el : '',
15440     
15441     /**
15442      * @cfg {String|Roo.Template} tpl The template used by this View 
15443      */
15444     tpl : false,
15445     /**
15446      * @cfg {String} dataName the named area of the template to use as the data area
15447      *                          Works with domtemplates roo-name="name"
15448      */
15449     dataName: false,
15450     /**
15451      * @cfg {String} selectedClass The css class to add to selected nodes
15452      */
15453     selectedClass : "x-view-selected",
15454      /**
15455      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15456      */
15457     emptyText : "",
15458     
15459     /**
15460      * @cfg {String} text to display on mask (default Loading)
15461      */
15462     mask : false,
15463     /**
15464      * @cfg {Boolean} multiSelect Allow multiple selection
15465      */
15466     multiSelect : false,
15467     /**
15468      * @cfg {Boolean} singleSelect Allow single selection
15469      */
15470     singleSelect:  false,
15471     
15472     /**
15473      * @cfg {Boolean} toggleSelect - selecting 
15474      */
15475     toggleSelect : false,
15476     
15477     /**
15478      * @cfg {Boolean} tickable - selecting 
15479      */
15480     tickable : false,
15481     
15482     /**
15483      * Returns the element this view is bound to.
15484      * @return {Roo.Element}
15485      */
15486     getEl : function(){
15487         return this.wrapEl;
15488     },
15489     
15490     
15491
15492     /**
15493      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15494      */
15495     refresh : function(){
15496         //Roo.log('refresh');
15497         var t = this.tpl;
15498         
15499         // if we are using something like 'domtemplate', then
15500         // the what gets used is:
15501         // t.applySubtemplate(NAME, data, wrapping data..)
15502         // the outer template then get' applied with
15503         //     the store 'extra data'
15504         // and the body get's added to the
15505         //      roo-name="data" node?
15506         //      <span class='roo-tpl-{name}'></span> ?????
15507         
15508         
15509         
15510         this.clearSelections();
15511         this.el.update("");
15512         var html = [];
15513         var records = this.store.getRange();
15514         if(records.length < 1) {
15515             
15516             // is this valid??  = should it render a template??
15517             
15518             this.el.update(this.emptyText);
15519             return;
15520         }
15521         var el = this.el;
15522         if (this.dataName) {
15523             this.el.update(t.apply(this.store.meta)); //????
15524             el = this.el.child('.roo-tpl-' + this.dataName);
15525         }
15526         
15527         for(var i = 0, len = records.length; i < len; i++){
15528             var data = this.prepareData(records[i].data, i, records[i]);
15529             this.fireEvent("preparedata", this, data, i, records[i]);
15530             
15531             var d = Roo.apply({}, data);
15532             
15533             if(this.tickable){
15534                 Roo.apply(d, {'roo-id' : Roo.id()});
15535                 
15536                 var _this = this;
15537             
15538                 Roo.each(this.parent.item, function(item){
15539                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15540                         return;
15541                     }
15542                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15543                 });
15544             }
15545             
15546             html[html.length] = Roo.util.Format.trim(
15547                 this.dataName ?
15548                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15549                     t.apply(d)
15550             );
15551         }
15552         
15553         
15554         
15555         el.update(html.join(""));
15556         this.nodes = el.dom.childNodes;
15557         this.updateIndexes(0);
15558     },
15559     
15560
15561     /**
15562      * Function to override to reformat the data that is sent to
15563      * the template for each node.
15564      * DEPRICATED - use the preparedata event handler.
15565      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15566      * a JSON object for an UpdateManager bound view).
15567      */
15568     prepareData : function(data, index, record)
15569     {
15570         this.fireEvent("preparedata", this, data, index, record);
15571         return data;
15572     },
15573
15574     onUpdate : function(ds, record){
15575         // Roo.log('on update');   
15576         this.clearSelections();
15577         var index = this.store.indexOf(record);
15578         var n = this.nodes[index];
15579         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15580         n.parentNode.removeChild(n);
15581         this.updateIndexes(index, index);
15582     },
15583
15584     
15585     
15586 // --------- FIXME     
15587     onAdd : function(ds, records, index)
15588     {
15589         //Roo.log(['on Add', ds, records, index] );        
15590         this.clearSelections();
15591         if(this.nodes.length == 0){
15592             this.refresh();
15593             return;
15594         }
15595         var n = this.nodes[index];
15596         for(var i = 0, len = records.length; i < len; i++){
15597             var d = this.prepareData(records[i].data, i, records[i]);
15598             if(n){
15599                 this.tpl.insertBefore(n, d);
15600             }else{
15601                 
15602                 this.tpl.append(this.el, d);
15603             }
15604         }
15605         this.updateIndexes(index);
15606     },
15607
15608     onRemove : function(ds, record, index){
15609        // Roo.log('onRemove');
15610         this.clearSelections();
15611         var el = this.dataName  ?
15612             this.el.child('.roo-tpl-' + this.dataName) :
15613             this.el; 
15614         
15615         el.dom.removeChild(this.nodes[index]);
15616         this.updateIndexes(index);
15617     },
15618
15619     /**
15620      * Refresh an individual node.
15621      * @param {Number} index
15622      */
15623     refreshNode : function(index){
15624         this.onUpdate(this.store, this.store.getAt(index));
15625     },
15626
15627     updateIndexes : function(startIndex, endIndex){
15628         var ns = this.nodes;
15629         startIndex = startIndex || 0;
15630         endIndex = endIndex || ns.length - 1;
15631         for(var i = startIndex; i <= endIndex; i++){
15632             ns[i].nodeIndex = i;
15633         }
15634     },
15635
15636     /**
15637      * Changes the data store this view uses and refresh the view.
15638      * @param {Store} store
15639      */
15640     setStore : function(store, initial){
15641         if(!initial && this.store){
15642             this.store.un("datachanged", this.refresh);
15643             this.store.un("add", this.onAdd);
15644             this.store.un("remove", this.onRemove);
15645             this.store.un("update", this.onUpdate);
15646             this.store.un("clear", this.refresh);
15647             this.store.un("beforeload", this.onBeforeLoad);
15648             this.store.un("load", this.onLoad);
15649             this.store.un("loadexception", this.onLoad);
15650         }
15651         if(store){
15652           
15653             store.on("datachanged", this.refresh, this);
15654             store.on("add", this.onAdd, this);
15655             store.on("remove", this.onRemove, this);
15656             store.on("update", this.onUpdate, this);
15657             store.on("clear", this.refresh, this);
15658             store.on("beforeload", this.onBeforeLoad, this);
15659             store.on("load", this.onLoad, this);
15660             store.on("loadexception", this.onLoad, this);
15661         }
15662         
15663         if(store){
15664             this.refresh();
15665         }
15666     },
15667     /**
15668      * onbeforeLoad - masks the loading area.
15669      *
15670      */
15671     onBeforeLoad : function(store,opts)
15672     {
15673          //Roo.log('onBeforeLoad');   
15674         if (!opts.add) {
15675             this.el.update("");
15676         }
15677         this.el.mask(this.mask ? this.mask : "Loading" ); 
15678     },
15679     onLoad : function ()
15680     {
15681         this.el.unmask();
15682     },
15683     
15684
15685     /**
15686      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15687      * @param {HTMLElement} node
15688      * @return {HTMLElement} The template node
15689      */
15690     findItemFromChild : function(node){
15691         var el = this.dataName  ?
15692             this.el.child('.roo-tpl-' + this.dataName,true) :
15693             this.el.dom; 
15694         
15695         if(!node || node.parentNode == el){
15696                     return node;
15697             }
15698             var p = node.parentNode;
15699             while(p && p != el){
15700             if(p.parentNode == el){
15701                 return p;
15702             }
15703             p = p.parentNode;
15704         }
15705             return null;
15706     },
15707
15708     /** @ignore */
15709     onClick : function(e){
15710         var item = this.findItemFromChild(e.getTarget());
15711         if(item){
15712             var index = this.indexOf(item);
15713             if(this.onItemClick(item, index, e) !== false){
15714                 this.fireEvent("click", this, index, item, e);
15715             }
15716         }else{
15717             this.clearSelections();
15718         }
15719     },
15720
15721     /** @ignore */
15722     onContextMenu : function(e){
15723         var item = this.findItemFromChild(e.getTarget());
15724         if(item){
15725             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15726         }
15727     },
15728
15729     /** @ignore */
15730     onDblClick : function(e){
15731         var item = this.findItemFromChild(e.getTarget());
15732         if(item){
15733             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15734         }
15735     },
15736
15737     onItemClick : function(item, index, e)
15738     {
15739         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15740             return false;
15741         }
15742         if (this.toggleSelect) {
15743             var m = this.isSelected(item) ? 'unselect' : 'select';
15744             //Roo.log(m);
15745             var _t = this;
15746             _t[m](item, true, false);
15747             return true;
15748         }
15749         if(this.multiSelect || this.singleSelect){
15750             if(this.multiSelect && e.shiftKey && this.lastSelection){
15751                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15752             }else{
15753                 this.select(item, this.multiSelect && e.ctrlKey);
15754                 this.lastSelection = item;
15755             }
15756             
15757             if(!this.tickable){
15758                 e.preventDefault();
15759             }
15760             
15761         }
15762         return true;
15763     },
15764
15765     /**
15766      * Get the number of selected nodes.
15767      * @return {Number}
15768      */
15769     getSelectionCount : function(){
15770         return this.selections.length;
15771     },
15772
15773     /**
15774      * Get the currently selected nodes.
15775      * @return {Array} An array of HTMLElements
15776      */
15777     getSelectedNodes : function(){
15778         return this.selections;
15779     },
15780
15781     /**
15782      * Get the indexes of the selected nodes.
15783      * @return {Array}
15784      */
15785     getSelectedIndexes : function(){
15786         var indexes = [], s = this.selections;
15787         for(var i = 0, len = s.length; i < len; i++){
15788             indexes.push(s[i].nodeIndex);
15789         }
15790         return indexes;
15791     },
15792
15793     /**
15794      * Clear all selections
15795      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15796      */
15797     clearSelections : function(suppressEvent){
15798         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15799             this.cmp.elements = this.selections;
15800             this.cmp.removeClass(this.selectedClass);
15801             this.selections = [];
15802             if(!suppressEvent){
15803                 this.fireEvent("selectionchange", this, this.selections);
15804             }
15805         }
15806     },
15807
15808     /**
15809      * Returns true if the passed node is selected
15810      * @param {HTMLElement/Number} node The node or node index
15811      * @return {Boolean}
15812      */
15813     isSelected : function(node){
15814         var s = this.selections;
15815         if(s.length < 1){
15816             return false;
15817         }
15818         node = this.getNode(node);
15819         return s.indexOf(node) !== -1;
15820     },
15821
15822     /**
15823      * Selects nodes.
15824      * @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
15825      * @param {Boolean} keepExisting (optional) true to keep existing selections
15826      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15827      */
15828     select : function(nodeInfo, keepExisting, suppressEvent){
15829         if(nodeInfo instanceof Array){
15830             if(!keepExisting){
15831                 this.clearSelections(true);
15832             }
15833             for(var i = 0, len = nodeInfo.length; i < len; i++){
15834                 this.select(nodeInfo[i], true, true);
15835             }
15836             return;
15837         } 
15838         var node = this.getNode(nodeInfo);
15839         if(!node || this.isSelected(node)){
15840             return; // already selected.
15841         }
15842         if(!keepExisting){
15843             this.clearSelections(true);
15844         }
15845         
15846         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15847             Roo.fly(node).addClass(this.selectedClass);
15848             this.selections.push(node);
15849             if(!suppressEvent){
15850                 this.fireEvent("selectionchange", this, this.selections);
15851             }
15852         }
15853         
15854         
15855     },
15856       /**
15857      * Unselects nodes.
15858      * @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
15859      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15860      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15861      */
15862     unselect : function(nodeInfo, keepExisting, suppressEvent)
15863     {
15864         if(nodeInfo instanceof Array){
15865             Roo.each(this.selections, function(s) {
15866                 this.unselect(s, nodeInfo);
15867             }, this);
15868             return;
15869         }
15870         var node = this.getNode(nodeInfo);
15871         if(!node || !this.isSelected(node)){
15872             //Roo.log("not selected");
15873             return; // not selected.
15874         }
15875         // fireevent???
15876         var ns = [];
15877         Roo.each(this.selections, function(s) {
15878             if (s == node ) {
15879                 Roo.fly(node).removeClass(this.selectedClass);
15880
15881                 return;
15882             }
15883             ns.push(s);
15884         },this);
15885         
15886         this.selections= ns;
15887         this.fireEvent("selectionchange", this, this.selections);
15888     },
15889
15890     /**
15891      * Gets a template node.
15892      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15893      * @return {HTMLElement} The node or null if it wasn't found
15894      */
15895     getNode : function(nodeInfo){
15896         if(typeof nodeInfo == "string"){
15897             return document.getElementById(nodeInfo);
15898         }else if(typeof nodeInfo == "number"){
15899             return this.nodes[nodeInfo];
15900         }
15901         return nodeInfo;
15902     },
15903
15904     /**
15905      * Gets a range template nodes.
15906      * @param {Number} startIndex
15907      * @param {Number} endIndex
15908      * @return {Array} An array of nodes
15909      */
15910     getNodes : function(start, end){
15911         var ns = this.nodes;
15912         start = start || 0;
15913         end = typeof end == "undefined" ? ns.length - 1 : end;
15914         var nodes = [];
15915         if(start <= end){
15916             for(var i = start; i <= end; i++){
15917                 nodes.push(ns[i]);
15918             }
15919         } else{
15920             for(var i = start; i >= end; i--){
15921                 nodes.push(ns[i]);
15922             }
15923         }
15924         return nodes;
15925     },
15926
15927     /**
15928      * Finds the index of the passed node
15929      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15930      * @return {Number} The index of the node or -1
15931      */
15932     indexOf : function(node){
15933         node = this.getNode(node);
15934         if(typeof node.nodeIndex == "number"){
15935             return node.nodeIndex;
15936         }
15937         var ns = this.nodes;
15938         for(var i = 0, len = ns.length; i < len; i++){
15939             if(ns[i] == node){
15940                 return i;
15941             }
15942         }
15943         return -1;
15944     }
15945 });
15946 /*
15947  * - LGPL
15948  *
15949  * based on jquery fullcalendar
15950  * 
15951  */
15952
15953 Roo.bootstrap = Roo.bootstrap || {};
15954 /**
15955  * @class Roo.bootstrap.Calendar
15956  * @extends Roo.bootstrap.Component
15957  * Bootstrap Calendar class
15958  * @cfg {Boolean} loadMask (true|false) default false
15959  * @cfg {Object} header generate the user specific header of the calendar, default false
15960
15961  * @constructor
15962  * Create a new Container
15963  * @param {Object} config The config object
15964  */
15965
15966
15967
15968 Roo.bootstrap.Calendar = function(config){
15969     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15970      this.addEvents({
15971         /**
15972              * @event select
15973              * Fires when a date is selected
15974              * @param {DatePicker} this
15975              * @param {Date} date The selected date
15976              */
15977         'select': true,
15978         /**
15979              * @event monthchange
15980              * Fires when the displayed month changes 
15981              * @param {DatePicker} this
15982              * @param {Date} date The selected month
15983              */
15984         'monthchange': true,
15985         /**
15986              * @event evententer
15987              * Fires when mouse over an event
15988              * @param {Calendar} this
15989              * @param {event} Event
15990              */
15991         'evententer': true,
15992         /**
15993              * @event eventleave
15994              * Fires when the mouse leaves an
15995              * @param {Calendar} this
15996              * @param {event}
15997              */
15998         'eventleave': true,
15999         /**
16000              * @event eventclick
16001              * Fires when the mouse click an
16002              * @param {Calendar} this
16003              * @param {event}
16004              */
16005         'eventclick': true
16006         
16007     });
16008
16009 };
16010
16011 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16012     
16013      /**
16014      * @cfg {Number} startDay
16015      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16016      */
16017     startDay : 0,
16018     
16019     loadMask : false,
16020     
16021     header : false,
16022       
16023     getAutoCreate : function(){
16024         
16025         
16026         var fc_button = function(name, corner, style, content ) {
16027             return Roo.apply({},{
16028                 tag : 'span',
16029                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16030                          (corner.length ?
16031                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16032                             ''
16033                         ),
16034                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16035                 unselectable: 'on'
16036             });
16037         };
16038         
16039         var header = {};
16040         
16041         if(!this.header){
16042             header = {
16043                 tag : 'table',
16044                 cls : 'fc-header',
16045                 style : 'width:100%',
16046                 cn : [
16047                     {
16048                         tag: 'tr',
16049                         cn : [
16050                             {
16051                                 tag : 'td',
16052                                 cls : 'fc-header-left',
16053                                 cn : [
16054                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16055                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16056                                     { tag: 'span', cls: 'fc-header-space' },
16057                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16058
16059
16060                                 ]
16061                             },
16062
16063                             {
16064                                 tag : 'td',
16065                                 cls : 'fc-header-center',
16066                                 cn : [
16067                                     {
16068                                         tag: 'span',
16069                                         cls: 'fc-header-title',
16070                                         cn : {
16071                                             tag: 'H2',
16072                                             html : 'month / year'
16073                                         }
16074                                     }
16075
16076                                 ]
16077                             },
16078                             {
16079                                 tag : 'td',
16080                                 cls : 'fc-header-right',
16081                                 cn : [
16082                               /*      fc_button('month', 'left', '', 'month' ),
16083                                     fc_button('week', '', '', 'week' ),
16084                                     fc_button('day', 'right', '', 'day' )
16085                                 */    
16086
16087                                 ]
16088                             }
16089
16090                         ]
16091                     }
16092                 ]
16093             };
16094         }
16095         
16096         header = this.header;
16097         
16098        
16099         var cal_heads = function() {
16100             var ret = [];
16101             // fixme - handle this.
16102             
16103             for (var i =0; i < Date.dayNames.length; i++) {
16104                 var d = Date.dayNames[i];
16105                 ret.push({
16106                     tag: 'th',
16107                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16108                     html : d.substring(0,3)
16109                 });
16110                 
16111             }
16112             ret[0].cls += ' fc-first';
16113             ret[6].cls += ' fc-last';
16114             return ret;
16115         };
16116         var cal_cell = function(n) {
16117             return  {
16118                 tag: 'td',
16119                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16120                 cn : [
16121                     {
16122                         cn : [
16123                             {
16124                                 cls: 'fc-day-number',
16125                                 html: 'D'
16126                             },
16127                             {
16128                                 cls: 'fc-day-content',
16129                              
16130                                 cn : [
16131                                      {
16132                                         style: 'position: relative;' // height: 17px;
16133                                     }
16134                                 ]
16135                             }
16136                             
16137                             
16138                         ]
16139                     }
16140                 ]
16141                 
16142             }
16143         };
16144         var cal_rows = function() {
16145             
16146             var ret = [];
16147             for (var r = 0; r < 6; r++) {
16148                 var row= {
16149                     tag : 'tr',
16150                     cls : 'fc-week',
16151                     cn : []
16152                 };
16153                 
16154                 for (var i =0; i < Date.dayNames.length; i++) {
16155                     var d = Date.dayNames[i];
16156                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16157
16158                 }
16159                 row.cn[0].cls+=' fc-first';
16160                 row.cn[0].cn[0].style = 'min-height:90px';
16161                 row.cn[6].cls+=' fc-last';
16162                 ret.push(row);
16163                 
16164             }
16165             ret[0].cls += ' fc-first';
16166             ret[4].cls += ' fc-prev-last';
16167             ret[5].cls += ' fc-last';
16168             return ret;
16169             
16170         };
16171         
16172         var cal_table = {
16173             tag: 'table',
16174             cls: 'fc-border-separate',
16175             style : 'width:100%',
16176             cellspacing  : 0,
16177             cn : [
16178                 { 
16179                     tag: 'thead',
16180                     cn : [
16181                         { 
16182                             tag: 'tr',
16183                             cls : 'fc-first fc-last',
16184                             cn : cal_heads()
16185                         }
16186                     ]
16187                 },
16188                 { 
16189                     tag: 'tbody',
16190                     cn : cal_rows()
16191                 }
16192                   
16193             ]
16194         };
16195          
16196          var cfg = {
16197             cls : 'fc fc-ltr',
16198             cn : [
16199                 header,
16200                 {
16201                     cls : 'fc-content',
16202                     style : "position: relative;",
16203                     cn : [
16204                         {
16205                             cls : 'fc-view fc-view-month fc-grid',
16206                             style : 'position: relative',
16207                             unselectable : 'on',
16208                             cn : [
16209                                 {
16210                                     cls : 'fc-event-container',
16211                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16212                                 },
16213                                 cal_table
16214                             ]
16215                         }
16216                     ]
16217     
16218                 }
16219            ] 
16220             
16221         };
16222         
16223          
16224         
16225         return cfg;
16226     },
16227     
16228     
16229     initEvents : function()
16230     {
16231         if(!this.store){
16232             throw "can not find store for calendar";
16233         }
16234         
16235         var mark = {
16236             tag: "div",
16237             cls:"x-dlg-mask",
16238             style: "text-align:center",
16239             cn: [
16240                 {
16241                     tag: "div",
16242                     style: "background-color:white;width:50%;margin:250 auto",
16243                     cn: [
16244                         {
16245                             tag: "img",
16246                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16247                         },
16248                         {
16249                             tag: "span",
16250                             html: "Loading"
16251                         }
16252                         
16253                     ]
16254                 }
16255             ]
16256         };
16257         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16258         
16259         var size = this.el.select('.fc-content', true).first().getSize();
16260         this.maskEl.setSize(size.width, size.height);
16261         this.maskEl.enableDisplayMode("block");
16262         if(!this.loadMask){
16263             this.maskEl.hide();
16264         }
16265         
16266         this.store = Roo.factory(this.store, Roo.data);
16267         this.store.on('load', this.onLoad, this);
16268         this.store.on('beforeload', this.onBeforeLoad, this);
16269         
16270         this.resize();
16271         
16272         this.cells = this.el.select('.fc-day',true);
16273         //Roo.log(this.cells);
16274         this.textNodes = this.el.query('.fc-day-number');
16275         this.cells.addClassOnOver('fc-state-hover');
16276         
16277         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16278         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16279         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16280         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16281         
16282         this.on('monthchange', this.onMonthChange, this);
16283         
16284         this.update(new Date().clearTime());
16285     },
16286     
16287     resize : function() {
16288         var sz  = this.el.getSize();
16289         
16290         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16291         this.el.select('.fc-day-content div',true).setHeight(34);
16292     },
16293     
16294     
16295     // private
16296     showPrevMonth : function(e){
16297         this.update(this.activeDate.add("mo", -1));
16298     },
16299     showToday : function(e){
16300         this.update(new Date().clearTime());
16301     },
16302     // private
16303     showNextMonth : function(e){
16304         this.update(this.activeDate.add("mo", 1));
16305     },
16306
16307     // private
16308     showPrevYear : function(){
16309         this.update(this.activeDate.add("y", -1));
16310     },
16311
16312     // private
16313     showNextYear : function(){
16314         this.update(this.activeDate.add("y", 1));
16315     },
16316
16317     
16318    // private
16319     update : function(date)
16320     {
16321         var vd = this.activeDate;
16322         this.activeDate = date;
16323 //        if(vd && this.el){
16324 //            var t = date.getTime();
16325 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16326 //                Roo.log('using add remove');
16327 //                
16328 //                this.fireEvent('monthchange', this, date);
16329 //                
16330 //                this.cells.removeClass("fc-state-highlight");
16331 //                this.cells.each(function(c){
16332 //                   if(c.dateValue == t){
16333 //                       c.addClass("fc-state-highlight");
16334 //                       setTimeout(function(){
16335 //                            try{c.dom.firstChild.focus();}catch(e){}
16336 //                       }, 50);
16337 //                       return false;
16338 //                   }
16339 //                   return true;
16340 //                });
16341 //                return;
16342 //            }
16343 //        }
16344         
16345         var days = date.getDaysInMonth();
16346         
16347         var firstOfMonth = date.getFirstDateOfMonth();
16348         var startingPos = firstOfMonth.getDay()-this.startDay;
16349         
16350         if(startingPos < this.startDay){
16351             startingPos += 7;
16352         }
16353         
16354         var pm = date.add(Date.MONTH, -1);
16355         var prevStart = pm.getDaysInMonth()-startingPos;
16356 //        
16357         this.cells = this.el.select('.fc-day',true);
16358         this.textNodes = this.el.query('.fc-day-number');
16359         this.cells.addClassOnOver('fc-state-hover');
16360         
16361         var cells = this.cells.elements;
16362         var textEls = this.textNodes;
16363         
16364         Roo.each(cells, function(cell){
16365             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16366         });
16367         
16368         days += startingPos;
16369
16370         // convert everything to numbers so it's fast
16371         var day = 86400000;
16372         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16373         //Roo.log(d);
16374         //Roo.log(pm);
16375         //Roo.log(prevStart);
16376         
16377         var today = new Date().clearTime().getTime();
16378         var sel = date.clearTime().getTime();
16379         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16380         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16381         var ddMatch = this.disabledDatesRE;
16382         var ddText = this.disabledDatesText;
16383         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16384         var ddaysText = this.disabledDaysText;
16385         var format = this.format;
16386         
16387         var setCellClass = function(cal, cell){
16388             cell.row = 0;
16389             cell.events = [];
16390             cell.more = [];
16391             //Roo.log('set Cell Class');
16392             cell.title = "";
16393             var t = d.getTime();
16394             
16395             //Roo.log(d);
16396             
16397             cell.dateValue = t;
16398             if(t == today){
16399                 cell.className += " fc-today";
16400                 cell.className += " fc-state-highlight";
16401                 cell.title = cal.todayText;
16402             }
16403             if(t == sel){
16404                 // disable highlight in other month..
16405                 //cell.className += " fc-state-highlight";
16406                 
16407             }
16408             // disabling
16409             if(t < min) {
16410                 cell.className = " fc-state-disabled";
16411                 cell.title = cal.minText;
16412                 return;
16413             }
16414             if(t > max) {
16415                 cell.className = " fc-state-disabled";
16416                 cell.title = cal.maxText;
16417                 return;
16418             }
16419             if(ddays){
16420                 if(ddays.indexOf(d.getDay()) != -1){
16421                     cell.title = ddaysText;
16422                     cell.className = " fc-state-disabled";
16423                 }
16424             }
16425             if(ddMatch && format){
16426                 var fvalue = d.dateFormat(format);
16427                 if(ddMatch.test(fvalue)){
16428                     cell.title = ddText.replace("%0", fvalue);
16429                     cell.className = " fc-state-disabled";
16430                 }
16431             }
16432             
16433             if (!cell.initialClassName) {
16434                 cell.initialClassName = cell.dom.className;
16435             }
16436             
16437             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16438         };
16439
16440         var i = 0;
16441         
16442         for(; i < startingPos; i++) {
16443             textEls[i].innerHTML = (++prevStart);
16444             d.setDate(d.getDate()+1);
16445             
16446             cells[i].className = "fc-past fc-other-month";
16447             setCellClass(this, cells[i]);
16448         }
16449         
16450         var intDay = 0;
16451         
16452         for(; i < days; i++){
16453             intDay = i - startingPos + 1;
16454             textEls[i].innerHTML = (intDay);
16455             d.setDate(d.getDate()+1);
16456             
16457             cells[i].className = ''; // "x-date-active";
16458             setCellClass(this, cells[i]);
16459         }
16460         var extraDays = 0;
16461         
16462         for(; i < 42; i++) {
16463             textEls[i].innerHTML = (++extraDays);
16464             d.setDate(d.getDate()+1);
16465             
16466             cells[i].className = "fc-future fc-other-month";
16467             setCellClass(this, cells[i]);
16468         }
16469         
16470         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16471         
16472         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16473         
16474         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16475         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16476         
16477         if(totalRows != 6){
16478             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16479             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16480         }
16481         
16482         this.fireEvent('monthchange', this, date);
16483         
16484         
16485         /*
16486         if(!this.internalRender){
16487             var main = this.el.dom.firstChild;
16488             var w = main.offsetWidth;
16489             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16490             Roo.fly(main).setWidth(w);
16491             this.internalRender = true;
16492             // opera does not respect the auto grow header center column
16493             // then, after it gets a width opera refuses to recalculate
16494             // without a second pass
16495             if(Roo.isOpera && !this.secondPass){
16496                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16497                 this.secondPass = true;
16498                 this.update.defer(10, this, [date]);
16499             }
16500         }
16501         */
16502         
16503     },
16504     
16505     findCell : function(dt) {
16506         dt = dt.clearTime().getTime();
16507         var ret = false;
16508         this.cells.each(function(c){
16509             //Roo.log("check " +c.dateValue + '?=' + dt);
16510             if(c.dateValue == dt){
16511                 ret = c;
16512                 return false;
16513             }
16514             return true;
16515         });
16516         
16517         return ret;
16518     },
16519     
16520     findCells : function(ev) {
16521         var s = ev.start.clone().clearTime().getTime();
16522        // Roo.log(s);
16523         var e= ev.end.clone().clearTime().getTime();
16524        // Roo.log(e);
16525         var ret = [];
16526         this.cells.each(function(c){
16527              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16528             
16529             if(c.dateValue > e){
16530                 return ;
16531             }
16532             if(c.dateValue < s){
16533                 return ;
16534             }
16535             ret.push(c);
16536         });
16537         
16538         return ret;    
16539     },
16540     
16541 //    findBestRow: function(cells)
16542 //    {
16543 //        var ret = 0;
16544 //        
16545 //        for (var i =0 ; i < cells.length;i++) {
16546 //            ret  = Math.max(cells[i].rows || 0,ret);
16547 //        }
16548 //        return ret;
16549 //        
16550 //    },
16551     
16552     
16553     addItem : function(ev)
16554     {
16555         // look for vertical location slot in
16556         var cells = this.findCells(ev);
16557         
16558 //        ev.row = this.findBestRow(cells);
16559         
16560         // work out the location.
16561         
16562         var crow = false;
16563         var rows = [];
16564         for(var i =0; i < cells.length; i++) {
16565             
16566             cells[i].row = cells[0].row;
16567             
16568             if(i == 0){
16569                 cells[i].row = cells[i].row + 1;
16570             }
16571             
16572             if (!crow) {
16573                 crow = {
16574                     start : cells[i],
16575                     end :  cells[i]
16576                 };
16577                 continue;
16578             }
16579             if (crow.start.getY() == cells[i].getY()) {
16580                 // on same row.
16581                 crow.end = cells[i];
16582                 continue;
16583             }
16584             // different row.
16585             rows.push(crow);
16586             crow = {
16587                 start: cells[i],
16588                 end : cells[i]
16589             };
16590             
16591         }
16592         
16593         rows.push(crow);
16594         ev.els = [];
16595         ev.rows = rows;
16596         ev.cells = cells;
16597         
16598         cells[0].events.push(ev);
16599         
16600         this.calevents.push(ev);
16601     },
16602     
16603     clearEvents: function() {
16604         
16605         if(!this.calevents){
16606             return;
16607         }
16608         
16609         Roo.each(this.cells.elements, function(c){
16610             c.row = 0;
16611             c.events = [];
16612             c.more = [];
16613         });
16614         
16615         Roo.each(this.calevents, function(e) {
16616             Roo.each(e.els, function(el) {
16617                 el.un('mouseenter' ,this.onEventEnter, this);
16618                 el.un('mouseleave' ,this.onEventLeave, this);
16619                 el.remove();
16620             },this);
16621         },this);
16622         
16623         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16624             e.remove();
16625         });
16626         
16627     },
16628     
16629     renderEvents: function()
16630     {   
16631         var _this = this;
16632         
16633         this.cells.each(function(c) {
16634             
16635             if(c.row < 5){
16636                 return;
16637             }
16638             
16639             var ev = c.events;
16640             
16641             var r = 4;
16642             if(c.row != c.events.length){
16643                 r = 4 - (4 - (c.row - c.events.length));
16644             }
16645             
16646             c.events = ev.slice(0, r);
16647             c.more = ev.slice(r);
16648             
16649             if(c.more.length && c.more.length == 1){
16650                 c.events.push(c.more.pop());
16651             }
16652             
16653             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16654             
16655         });
16656             
16657         this.cells.each(function(c) {
16658             
16659             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16660             
16661             
16662             for (var e = 0; e < c.events.length; e++){
16663                 var ev = c.events[e];
16664                 var rows = ev.rows;
16665                 
16666                 for(var i = 0; i < rows.length; i++) {
16667                 
16668                     // how many rows should it span..
16669
16670                     var  cfg = {
16671                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16672                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16673
16674                         unselectable : "on",
16675                         cn : [
16676                             {
16677                                 cls: 'fc-event-inner',
16678                                 cn : [
16679     //                                {
16680     //                                  tag:'span',
16681     //                                  cls: 'fc-event-time',
16682     //                                  html : cells.length > 1 ? '' : ev.time
16683     //                                },
16684                                     {
16685                                       tag:'span',
16686                                       cls: 'fc-event-title',
16687                                       html : String.format('{0}', ev.title)
16688                                     }
16689
16690
16691                                 ]
16692                             },
16693                             {
16694                                 cls: 'ui-resizable-handle ui-resizable-e',
16695                                 html : '&nbsp;&nbsp;&nbsp'
16696                             }
16697
16698                         ]
16699                     };
16700
16701                     if (i == 0) {
16702                         cfg.cls += ' fc-event-start';
16703                     }
16704                     if ((i+1) == rows.length) {
16705                         cfg.cls += ' fc-event-end';
16706                     }
16707
16708                     var ctr = _this.el.select('.fc-event-container',true).first();
16709                     var cg = ctr.createChild(cfg);
16710
16711                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16712                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16713
16714                     var r = (c.more.length) ? 1 : 0;
16715                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16716                     cg.setWidth(ebox.right - sbox.x -2);
16717
16718                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16719                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16720                     cg.on('click', _this.onEventClick, _this, ev);
16721
16722                     ev.els.push(cg);
16723                     
16724                 }
16725                 
16726             }
16727             
16728             
16729             if(c.more.length){
16730                 var  cfg = {
16731                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16732                     style : 'position: absolute',
16733                     unselectable : "on",
16734                     cn : [
16735                         {
16736                             cls: 'fc-event-inner',
16737                             cn : [
16738                                 {
16739                                   tag:'span',
16740                                   cls: 'fc-event-title',
16741                                   html : 'More'
16742                                 }
16743
16744
16745                             ]
16746                         },
16747                         {
16748                             cls: 'ui-resizable-handle ui-resizable-e',
16749                             html : '&nbsp;&nbsp;&nbsp'
16750                         }
16751
16752                     ]
16753                 };
16754
16755                 var ctr = _this.el.select('.fc-event-container',true).first();
16756                 var cg = ctr.createChild(cfg);
16757
16758                 var sbox = c.select('.fc-day-content',true).first().getBox();
16759                 var ebox = c.select('.fc-day-content',true).first().getBox();
16760                 //Roo.log(cg);
16761                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16762                 cg.setWidth(ebox.right - sbox.x -2);
16763
16764                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16765                 
16766             }
16767             
16768         });
16769         
16770         
16771         
16772     },
16773     
16774     onEventEnter: function (e, el,event,d) {
16775         this.fireEvent('evententer', this, el, event);
16776     },
16777     
16778     onEventLeave: function (e, el,event,d) {
16779         this.fireEvent('eventleave', this, el, event);
16780     },
16781     
16782     onEventClick: function (e, el,event,d) {
16783         this.fireEvent('eventclick', this, el, event);
16784     },
16785     
16786     onMonthChange: function () {
16787         this.store.load();
16788     },
16789     
16790     onMoreEventClick: function(e, el, more)
16791     {
16792         var _this = this;
16793         
16794         this.calpopover.placement = 'right';
16795         this.calpopover.setTitle('More');
16796         
16797         this.calpopover.setContent('');
16798         
16799         var ctr = this.calpopover.el.select('.popover-content', true).first();
16800         
16801         Roo.each(more, function(m){
16802             var cfg = {
16803                 cls : 'fc-event-hori fc-event-draggable',
16804                 html : m.title
16805             };
16806             var cg = ctr.createChild(cfg);
16807             
16808             cg.on('click', _this.onEventClick, _this, m);
16809         });
16810         
16811         this.calpopover.show(el);
16812         
16813         
16814     },
16815     
16816     onLoad: function () 
16817     {   
16818         this.calevents = [];
16819         var cal = this;
16820         
16821         if(this.store.getCount() > 0){
16822             this.store.data.each(function(d){
16823                cal.addItem({
16824                     id : d.data.id,
16825                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16826                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16827                     time : d.data.start_time,
16828                     title : d.data.title,
16829                     description : d.data.description,
16830                     venue : d.data.venue
16831                 });
16832             });
16833         }
16834         
16835         this.renderEvents();
16836         
16837         if(this.calevents.length && this.loadMask){
16838             this.maskEl.hide();
16839         }
16840     },
16841     
16842     onBeforeLoad: function()
16843     {
16844         this.clearEvents();
16845         if(this.loadMask){
16846             this.maskEl.show();
16847         }
16848     }
16849 });
16850
16851  
16852  /*
16853  * - LGPL
16854  *
16855  * element
16856  * 
16857  */
16858
16859 /**
16860  * @class Roo.bootstrap.Popover
16861  * @extends Roo.bootstrap.Component
16862  * Bootstrap Popover class
16863  * @cfg {String} html contents of the popover   (or false to use children..)
16864  * @cfg {String} title of popover (or false to hide)
16865  * @cfg {String} placement how it is placed
16866  * @cfg {String} trigger click || hover (or false to trigger manually)
16867  * @cfg {String} over what (parent or false to trigger manually.)
16868  * @cfg {Number} delay - delay before showing
16869  
16870  * @constructor
16871  * Create a new Popover
16872  * @param {Object} config The config object
16873  */
16874
16875 Roo.bootstrap.Popover = function(config){
16876     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16877     
16878     this.addEvents({
16879         // raw events
16880          /**
16881          * @event show
16882          * After the popover show
16883          * 
16884          * @param {Roo.bootstrap.Popover} this
16885          */
16886         "show" : true,
16887         /**
16888          * @event hide
16889          * After the popover hide
16890          * 
16891          * @param {Roo.bootstrap.Popover} this
16892          */
16893         "hide" : true
16894     });
16895 };
16896
16897 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
16898     
16899     title: 'Fill in a title',
16900     html: false,
16901     
16902     placement : 'right',
16903     trigger : 'hover', // hover
16904     
16905     delay : 0,
16906     
16907     over: 'parent',
16908     
16909     can_build_overlaid : false,
16910     
16911     getChildContainer : function()
16912     {
16913         return this.el.select('.popover-content',true).first();
16914     },
16915     
16916     getAutoCreate : function(){
16917          
16918         var cfg = {
16919            cls : 'popover roo-dynamic',
16920            style: 'display:block',
16921            cn : [
16922                 {
16923                     cls : 'arrow'
16924                 },
16925                 {
16926                     cls : 'popover-inner',
16927                     cn : [
16928                         {
16929                             tag: 'h3',
16930                             cls: 'popover-title',
16931                             html : this.title
16932                         },
16933                         {
16934                             cls : 'popover-content',
16935                             html : this.html
16936                         }
16937                     ]
16938                     
16939                 }
16940            ]
16941         };
16942         
16943         return cfg;
16944     },
16945     setTitle: function(str)
16946     {
16947         this.title = str;
16948         this.el.select('.popover-title',true).first().dom.innerHTML = str;
16949     },
16950     setContent: function(str)
16951     {
16952         this.html = str;
16953         this.el.select('.popover-content',true).first().dom.innerHTML = str;
16954     },
16955     // as it get's added to the bottom of the page.
16956     onRender : function(ct, position)
16957     {
16958         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16959         if(!this.el){
16960             var cfg = Roo.apply({},  this.getAutoCreate());
16961             cfg.id = Roo.id();
16962             
16963             if (this.cls) {
16964                 cfg.cls += ' ' + this.cls;
16965             }
16966             if (this.style) {
16967                 cfg.style = this.style;
16968             }
16969             //Roo.log("adding to ");
16970             this.el = Roo.get(document.body).createChild(cfg, position);
16971 //            Roo.log(this.el);
16972         }
16973         this.initEvents();
16974     },
16975     
16976     initEvents : function()
16977     {
16978         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16979         this.el.enableDisplayMode('block');
16980         this.el.hide();
16981         if (this.over === false) {
16982             return; 
16983         }
16984         if (this.triggers === false) {
16985             return;
16986         }
16987         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16988         var triggers = this.trigger ? this.trigger.split(' ') : [];
16989         Roo.each(triggers, function(trigger) {
16990         
16991             if (trigger == 'click') {
16992                 on_el.on('click', this.toggle, this);
16993             } else if (trigger != 'manual') {
16994                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
16995                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16996       
16997                 on_el.on(eventIn  ,this.enter, this);
16998                 on_el.on(eventOut, this.leave, this);
16999             }
17000         }, this);
17001         
17002     },
17003     
17004     
17005     // private
17006     timeout : null,
17007     hoverState : null,
17008     
17009     toggle : function () {
17010         this.hoverState == 'in' ? this.leave() : this.enter();
17011     },
17012     
17013     enter : function () {
17014         
17015         clearTimeout(this.timeout);
17016     
17017         this.hoverState = 'in';
17018     
17019         if (!this.delay || !this.delay.show) {
17020             this.show();
17021             return;
17022         }
17023         var _t = this;
17024         this.timeout = setTimeout(function () {
17025             if (_t.hoverState == 'in') {
17026                 _t.show();
17027             }
17028         }, this.delay.show)
17029     },
17030     
17031     leave : function() {
17032         clearTimeout(this.timeout);
17033     
17034         this.hoverState = 'out';
17035     
17036         if (!this.delay || !this.delay.hide) {
17037             this.hide();
17038             return;
17039         }
17040         var _t = this;
17041         this.timeout = setTimeout(function () {
17042             if (_t.hoverState == 'out') {
17043                 _t.hide();
17044             }
17045         }, this.delay.hide)
17046     },
17047     
17048     show : function (on_el)
17049     {
17050         if (!on_el) {
17051             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17052         }
17053         
17054         // set content.
17055         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17056         if (this.html !== false) {
17057             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17058         }
17059         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17060         if (!this.title.length) {
17061             this.el.select('.popover-title',true).hide();
17062         }
17063         
17064         var placement = typeof this.placement == 'function' ?
17065             this.placement.call(this, this.el, on_el) :
17066             this.placement;
17067             
17068         var autoToken = /\s?auto?\s?/i;
17069         var autoPlace = autoToken.test(placement);
17070         if (autoPlace) {
17071             placement = placement.replace(autoToken, '') || 'top';
17072         }
17073         
17074         //this.el.detach()
17075         //this.el.setXY([0,0]);
17076         this.el.show();
17077         this.el.dom.style.display='block';
17078         this.el.addClass(placement);
17079         
17080         //this.el.appendTo(on_el);
17081         
17082         var p = this.getPosition();
17083         var box = this.el.getBox();
17084         
17085         if (autoPlace) {
17086             // fixme..
17087         }
17088         var align = Roo.bootstrap.Popover.alignment[placement];
17089         this.el.alignTo(on_el, align[0],align[1]);
17090         //var arrow = this.el.select('.arrow',true).first();
17091         //arrow.set(align[2], 
17092         
17093         this.el.addClass('in');
17094         
17095         
17096         if (this.el.hasClass('fade')) {
17097             // fade it?
17098         }
17099         
17100         this.hoverState = 'in';
17101         
17102         this.fireEvent('show', this);
17103         
17104     },
17105     hide : function()
17106     {
17107         this.el.setXY([0,0]);
17108         this.el.removeClass('in');
17109         this.el.hide();
17110         this.hoverState = null;
17111         
17112         this.fireEvent('hide', this);
17113     }
17114     
17115 });
17116
17117 Roo.bootstrap.Popover.alignment = {
17118     'left' : ['r-l', [-10,0], 'right'],
17119     'right' : ['l-r', [10,0], 'left'],
17120     'bottom' : ['t-b', [0,10], 'top'],
17121     'top' : [ 'b-t', [0,-10], 'bottom']
17122 };
17123
17124  /*
17125  * - LGPL
17126  *
17127  * Progress
17128  * 
17129  */
17130
17131 /**
17132  * @class Roo.bootstrap.Progress
17133  * @extends Roo.bootstrap.Component
17134  * Bootstrap Progress class
17135  * @cfg {Boolean} striped striped of the progress bar
17136  * @cfg {Boolean} active animated of the progress bar
17137  * 
17138  * 
17139  * @constructor
17140  * Create a new Progress
17141  * @param {Object} config The config object
17142  */
17143
17144 Roo.bootstrap.Progress = function(config){
17145     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17146 };
17147
17148 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17149     
17150     striped : false,
17151     active: false,
17152     
17153     getAutoCreate : function(){
17154         var cfg = {
17155             tag: 'div',
17156             cls: 'progress'
17157         };
17158         
17159         
17160         if(this.striped){
17161             cfg.cls += ' progress-striped';
17162         }
17163       
17164         if(this.active){
17165             cfg.cls += ' active';
17166         }
17167         
17168         
17169         return cfg;
17170     }
17171    
17172 });
17173
17174  
17175
17176  /*
17177  * - LGPL
17178  *
17179  * ProgressBar
17180  * 
17181  */
17182
17183 /**
17184  * @class Roo.bootstrap.ProgressBar
17185  * @extends Roo.bootstrap.Component
17186  * Bootstrap ProgressBar class
17187  * @cfg {Number} aria_valuenow aria-value now
17188  * @cfg {Number} aria_valuemin aria-value min
17189  * @cfg {Number} aria_valuemax aria-value max
17190  * @cfg {String} label label for the progress bar
17191  * @cfg {String} panel (success | info | warning | danger )
17192  * @cfg {String} role role of the progress bar
17193  * @cfg {String} sr_only text
17194  * 
17195  * 
17196  * @constructor
17197  * Create a new ProgressBar
17198  * @param {Object} config The config object
17199  */
17200
17201 Roo.bootstrap.ProgressBar = function(config){
17202     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17203 };
17204
17205 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17206     
17207     aria_valuenow : 0,
17208     aria_valuemin : 0,
17209     aria_valuemax : 100,
17210     label : false,
17211     panel : false,
17212     role : false,
17213     sr_only: false,
17214     
17215     getAutoCreate : function()
17216     {
17217         
17218         var cfg = {
17219             tag: 'div',
17220             cls: 'progress-bar',
17221             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17222         };
17223         
17224         if(this.sr_only){
17225             cfg.cn = {
17226                 tag: 'span',
17227                 cls: 'sr-only',
17228                 html: this.sr_only
17229             }
17230         }
17231         
17232         if(this.role){
17233             cfg.role = this.role;
17234         }
17235         
17236         if(this.aria_valuenow){
17237             cfg['aria-valuenow'] = this.aria_valuenow;
17238         }
17239         
17240         if(this.aria_valuemin){
17241             cfg['aria-valuemin'] = this.aria_valuemin;
17242         }
17243         
17244         if(this.aria_valuemax){
17245             cfg['aria-valuemax'] = this.aria_valuemax;
17246         }
17247         
17248         if(this.label && !this.sr_only){
17249             cfg.html = this.label;
17250         }
17251         
17252         if(this.panel){
17253             cfg.cls += ' progress-bar-' + this.panel;
17254         }
17255         
17256         return cfg;
17257     },
17258     
17259     update : function(aria_valuenow)
17260     {
17261         this.aria_valuenow = aria_valuenow;
17262         
17263         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17264     }
17265    
17266 });
17267
17268  
17269
17270  /*
17271  * - LGPL
17272  *
17273  * column
17274  * 
17275  */
17276
17277 /**
17278  * @class Roo.bootstrap.TabGroup
17279  * @extends Roo.bootstrap.Column
17280  * Bootstrap Column class
17281  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17282  * @cfg {Boolean} carousel true to make the group behave like a carousel
17283  * @cfg {Boolean} bullets show bullets for the panels
17284  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17285  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17286  * @cfg {Boolean} showarrow (true|false) show arrow default true
17287  * 
17288  * @constructor
17289  * Create a new TabGroup
17290  * @param {Object} config The config object
17291  */
17292
17293 Roo.bootstrap.TabGroup = function(config){
17294     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17295     if (!this.navId) {
17296         this.navId = Roo.id();
17297     }
17298     this.tabs = [];
17299     Roo.bootstrap.TabGroup.register(this);
17300     
17301 };
17302
17303 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17304     
17305     carousel : false,
17306     transition : false,
17307     bullets : 0,
17308     timer : 0,
17309     autoslide : false,
17310     slideFn : false,
17311     slideOnTouch : false,
17312     showarrow : true,
17313     
17314     getAutoCreate : function()
17315     {
17316         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17317         
17318         cfg.cls += ' tab-content';
17319         
17320         if (this.carousel) {
17321             cfg.cls += ' carousel slide';
17322             
17323             cfg.cn = [{
17324                cls : 'carousel-inner',
17325                cn : []
17326             }];
17327         
17328             if(this.bullets  && !Roo.isTouch){
17329                 
17330                 var bullets = {
17331                     cls : 'carousel-bullets',
17332                     cn : []
17333                 };
17334                
17335                 if(this.bullets_cls){
17336                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17337                 }
17338                 
17339                 bullets.cn.push({
17340                     cls : 'clear'
17341                 });
17342                 
17343                 cfg.cn[0].cn.push(bullets);
17344             }
17345             
17346             if(this.showarrow){
17347                 cfg.cn[0].cn.push({
17348                     tag : 'div',
17349                     class : 'carousel-arrow',
17350                     cn : [
17351                         {
17352                             tag : 'div',
17353                             class : 'carousel-prev',
17354                             cn : [
17355                                 {
17356                                     tag : 'i',
17357                                     class : 'fa fa-chevron-left'
17358                                 }
17359                             ]
17360                         },
17361                         {
17362                             tag : 'div',
17363                             class : 'carousel-next',
17364                             cn : [
17365                                 {
17366                                     tag : 'i',
17367                                     class : 'fa fa-chevron-right'
17368                                 }
17369                             ]
17370                         }
17371                     ]
17372                 });
17373             }
17374             
17375         }
17376         
17377         return cfg;
17378     },
17379     
17380     initEvents:  function()
17381     {
17382 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17383 //            this.el.on("touchstart", this.onTouchStart, this);
17384 //        }
17385         
17386         if(this.autoslide){
17387             var _this = this;
17388             
17389             this.slideFn = window.setInterval(function() {
17390                 _this.showPanelNext();
17391             }, this.timer);
17392         }
17393         
17394         if(this.showarrow){
17395             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17396             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17397         }
17398         
17399         
17400     },
17401     
17402 //    onTouchStart : function(e, el, o)
17403 //    {
17404 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17405 //            return;
17406 //        }
17407 //        
17408 //        this.showPanelNext();
17409 //    },
17410     
17411     
17412     getChildContainer : function()
17413     {
17414         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17415     },
17416     
17417     /**
17418     * register a Navigation item
17419     * @param {Roo.bootstrap.NavItem} the navitem to add
17420     */
17421     register : function(item)
17422     {
17423         this.tabs.push( item);
17424         item.navId = this.navId; // not really needed..
17425         this.addBullet();
17426     
17427     },
17428     
17429     getActivePanel : function()
17430     {
17431         var r = false;
17432         Roo.each(this.tabs, function(t) {
17433             if (t.active) {
17434                 r = t;
17435                 return false;
17436             }
17437             return null;
17438         });
17439         return r;
17440         
17441     },
17442     getPanelByName : function(n)
17443     {
17444         var r = false;
17445         Roo.each(this.tabs, function(t) {
17446             if (t.tabId == n) {
17447                 r = t;
17448                 return false;
17449             }
17450             return null;
17451         });
17452         return r;
17453     },
17454     indexOfPanel : function(p)
17455     {
17456         var r = false;
17457         Roo.each(this.tabs, function(t,i) {
17458             if (t.tabId == p.tabId) {
17459                 r = i;
17460                 return false;
17461             }
17462             return null;
17463         });
17464         return r;
17465     },
17466     /**
17467      * show a specific panel
17468      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17469      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17470      */
17471     showPanel : function (pan)
17472     {
17473         if(this.transition || typeof(pan) == 'undefined'){
17474             Roo.log("waiting for the transitionend");
17475             return;
17476         }
17477         
17478         if (typeof(pan) == 'number') {
17479             pan = this.tabs[pan];
17480         }
17481         
17482         if (typeof(pan) == 'string') {
17483             pan = this.getPanelByName(pan);
17484         }
17485         
17486         var cur = this.getActivePanel();
17487         
17488         if(!pan || !cur){
17489             Roo.log('pan or acitve pan is undefined');
17490             return false;
17491         }
17492         
17493         if (pan.tabId == this.getActivePanel().tabId) {
17494             return true;
17495         }
17496         
17497         if (false === cur.fireEvent('beforedeactivate')) {
17498             return false;
17499         }
17500         
17501         if(this.bullets > 0 && !Roo.isTouch){
17502             this.setActiveBullet(this.indexOfPanel(pan));
17503         }
17504         
17505         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17506             
17507             this.transition = true;
17508             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17509             var lr = dir == 'next' ? 'left' : 'right';
17510             pan.el.addClass(dir); // or prev
17511             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17512             cur.el.addClass(lr); // or right
17513             pan.el.addClass(lr);
17514             
17515             var _this = this;
17516             cur.el.on('transitionend', function() {
17517                 Roo.log("trans end?");
17518                 
17519                 pan.el.removeClass([lr,dir]);
17520                 pan.setActive(true);
17521                 
17522                 cur.el.removeClass([lr]);
17523                 cur.setActive(false);
17524                 
17525                 _this.transition = false;
17526                 
17527             }, this, { single:  true } );
17528             
17529             return true;
17530         }
17531         
17532         cur.setActive(false);
17533         pan.setActive(true);
17534         
17535         return true;
17536         
17537     },
17538     showPanelNext : function()
17539     {
17540         var i = this.indexOfPanel(this.getActivePanel());
17541         
17542         if (i >= this.tabs.length - 1 && !this.autoslide) {
17543             return;
17544         }
17545         
17546         if (i >= this.tabs.length - 1 && this.autoslide) {
17547             i = -1;
17548         }
17549         
17550         this.showPanel(this.tabs[i+1]);
17551     },
17552     
17553     showPanelPrev : function()
17554     {
17555         var i = this.indexOfPanel(this.getActivePanel());
17556         
17557         if (i  < 1 && !this.autoslide) {
17558             return;
17559         }
17560         
17561         if (i < 1 && this.autoslide) {
17562             i = this.tabs.length;
17563         }
17564         
17565         this.showPanel(this.tabs[i-1]);
17566     },
17567     
17568     
17569     addBullet: function()
17570     {
17571         if(!this.bullets || Roo.isTouch){
17572             return;
17573         }
17574         var ctr = this.el.select('.carousel-bullets',true).first();
17575         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17576         var bullet = ctr.createChild({
17577             cls : 'bullet bullet-' + i
17578         },ctr.dom.lastChild);
17579         
17580         
17581         var _this = this;
17582         
17583         bullet.on('click', (function(e, el, o, ii, t){
17584
17585             e.preventDefault();
17586
17587             this.showPanel(ii);
17588
17589             if(this.autoslide && this.slideFn){
17590                 clearInterval(this.slideFn);
17591                 this.slideFn = window.setInterval(function() {
17592                     _this.showPanelNext();
17593                 }, this.timer);
17594             }
17595
17596         }).createDelegate(this, [i, bullet], true));
17597                 
17598         
17599     },
17600      
17601     setActiveBullet : function(i)
17602     {
17603         if(Roo.isTouch){
17604             return;
17605         }
17606         
17607         Roo.each(this.el.select('.bullet', true).elements, function(el){
17608             el.removeClass('selected');
17609         });
17610
17611         var bullet = this.el.select('.bullet-' + i, true).first();
17612         
17613         if(!bullet){
17614             return;
17615         }
17616         
17617         bullet.addClass('selected');
17618     }
17619     
17620     
17621   
17622 });
17623
17624  
17625
17626  
17627  
17628 Roo.apply(Roo.bootstrap.TabGroup, {
17629     
17630     groups: {},
17631      /**
17632     * register a Navigation Group
17633     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17634     */
17635     register : function(navgrp)
17636     {
17637         this.groups[navgrp.navId] = navgrp;
17638         
17639     },
17640     /**
17641     * fetch a Navigation Group based on the navigation ID
17642     * if one does not exist , it will get created.
17643     * @param {string} the navgroup to add
17644     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17645     */
17646     get: function(navId) {
17647         if (typeof(this.groups[navId]) == 'undefined') {
17648             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17649         }
17650         return this.groups[navId] ;
17651     }
17652     
17653     
17654     
17655 });
17656
17657  /*
17658  * - LGPL
17659  *
17660  * TabPanel
17661  * 
17662  */
17663
17664 /**
17665  * @class Roo.bootstrap.TabPanel
17666  * @extends Roo.bootstrap.Component
17667  * Bootstrap TabPanel class
17668  * @cfg {Boolean} active panel active
17669  * @cfg {String} html panel content
17670  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17671  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17672  * @cfg {String} href click to link..
17673  * 
17674  * 
17675  * @constructor
17676  * Create a new TabPanel
17677  * @param {Object} config The config object
17678  */
17679
17680 Roo.bootstrap.TabPanel = function(config){
17681     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17682     this.addEvents({
17683         /**
17684              * @event changed
17685              * Fires when the active status changes
17686              * @param {Roo.bootstrap.TabPanel} this
17687              * @param {Boolean} state the new state
17688             
17689          */
17690         'changed': true,
17691         /**
17692              * @event beforedeactivate
17693              * Fires before a tab is de-activated - can be used to do validation on a form.
17694              * @param {Roo.bootstrap.TabPanel} this
17695              * @return {Boolean} false if there is an error
17696             
17697          */
17698         'beforedeactivate': true
17699      });
17700     
17701     this.tabId = this.tabId || Roo.id();
17702   
17703 };
17704
17705 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17706     
17707     active: false,
17708     html: false,
17709     tabId: false,
17710     navId : false,
17711     href : '',
17712     
17713     getAutoCreate : function(){
17714         var cfg = {
17715             tag: 'div',
17716             // item is needed for carousel - not sure if it has any effect otherwise
17717             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17718             html: this.html || ''
17719         };
17720         
17721         if(this.active){
17722             cfg.cls += ' active';
17723         }
17724         
17725         if(this.tabId){
17726             cfg.tabId = this.tabId;
17727         }
17728         
17729         
17730         return cfg;
17731     },
17732     
17733     initEvents:  function()
17734     {
17735         var p = this.parent();
17736         
17737         this.navId = this.navId || p.navId;
17738         
17739         if (typeof(this.navId) != 'undefined') {
17740             // not really needed.. but just in case.. parent should be a NavGroup.
17741             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17742             
17743             tg.register(this);
17744             
17745             var i = tg.tabs.length - 1;
17746             
17747             if(this.active && tg.bullets > 0 && i < tg.bullets){
17748                 tg.setActiveBullet(i);
17749             }
17750         }
17751         
17752         this.el.on('click', this.onClick, this);
17753         
17754         if(Roo.isTouch){
17755             this.el.on("touchstart", this.onTouchStart, this);
17756             this.el.on("touchmove", this.onTouchMove, this);
17757             this.el.on("touchend", this.onTouchEnd, this);
17758         }
17759         
17760     },
17761     
17762     onRender : function(ct, position)
17763     {
17764         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17765     },
17766     
17767     setActive : function(state)
17768     {
17769         Roo.log("panel - set active " + this.tabId + "=" + state);
17770         
17771         this.active = state;
17772         if (!state) {
17773             this.el.removeClass('active');
17774             
17775         } else  if (!this.el.hasClass('active')) {
17776             this.el.addClass('active');
17777         }
17778         
17779         this.fireEvent('changed', this, state);
17780     },
17781     
17782     onClick : function(e)
17783     {
17784         e.preventDefault();
17785         
17786         if(!this.href.length){
17787             return;
17788         }
17789         
17790         window.location.href = this.href;
17791     },
17792     
17793     startX : 0,
17794     startY : 0,
17795     endX : 0,
17796     endY : 0,
17797     swiping : false,
17798     
17799     onTouchStart : function(e)
17800     {
17801         this.swiping = false;
17802         
17803         this.startX = e.browserEvent.touches[0].clientX;
17804         this.startY = e.browserEvent.touches[0].clientY;
17805     },
17806     
17807     onTouchMove : function(e)
17808     {
17809         this.swiping = true;
17810         
17811         this.endX = e.browserEvent.touches[0].clientX;
17812         this.endY = e.browserEvent.touches[0].clientY;
17813     },
17814     
17815     onTouchEnd : function(e)
17816     {
17817         if(!this.swiping){
17818             this.onClick(e);
17819             return;
17820         }
17821         
17822         var tabGroup = this.parent();
17823         
17824         if(this.endX > this.startX){ // swiping right
17825             tabGroup.showPanelPrev();
17826             return;
17827         }
17828         
17829         if(this.startX > this.endX){ // swiping left
17830             tabGroup.showPanelNext();
17831             return;
17832         }
17833     }
17834     
17835     
17836 });
17837  
17838
17839  
17840
17841  /*
17842  * - LGPL
17843  *
17844  * DateField
17845  * 
17846  */
17847
17848 /**
17849  * @class Roo.bootstrap.DateField
17850  * @extends Roo.bootstrap.Input
17851  * Bootstrap DateField class
17852  * @cfg {Number} weekStart default 0
17853  * @cfg {String} viewMode default empty, (months|years)
17854  * @cfg {String} minViewMode default empty, (months|years)
17855  * @cfg {Number} startDate default -Infinity
17856  * @cfg {Number} endDate default Infinity
17857  * @cfg {Boolean} todayHighlight default false
17858  * @cfg {Boolean} todayBtn default false
17859  * @cfg {Boolean} calendarWeeks default false
17860  * @cfg {Object} daysOfWeekDisabled default empty
17861  * @cfg {Boolean} singleMode default false (true | false)
17862  * 
17863  * @cfg {Boolean} keyboardNavigation default true
17864  * @cfg {String} language default en
17865  * 
17866  * @constructor
17867  * Create a new DateField
17868  * @param {Object} config The config object
17869  */
17870
17871 Roo.bootstrap.DateField = function(config){
17872     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17873      this.addEvents({
17874             /**
17875              * @event show
17876              * Fires when this field show.
17877              * @param {Roo.bootstrap.DateField} this
17878              * @param {Mixed} date The date value
17879              */
17880             show : true,
17881             /**
17882              * @event show
17883              * Fires when this field hide.
17884              * @param {Roo.bootstrap.DateField} this
17885              * @param {Mixed} date The date value
17886              */
17887             hide : true,
17888             /**
17889              * @event select
17890              * Fires when select a date.
17891              * @param {Roo.bootstrap.DateField} this
17892              * @param {Mixed} date The date value
17893              */
17894             select : true,
17895             /**
17896              * @event beforeselect
17897              * Fires when before select a date.
17898              * @param {Roo.bootstrap.DateField} this
17899              * @param {Mixed} date The date value
17900              */
17901             beforeselect : true
17902         });
17903 };
17904
17905 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
17906     
17907     /**
17908      * @cfg {String} format
17909      * The default date format string which can be overriden for localization support.  The format must be
17910      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17911      */
17912     format : "m/d/y",
17913     /**
17914      * @cfg {String} altFormats
17915      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17916      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17917      */
17918     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17919     
17920     weekStart : 0,
17921     
17922     viewMode : '',
17923     
17924     minViewMode : '',
17925     
17926     todayHighlight : false,
17927     
17928     todayBtn: false,
17929     
17930     language: 'en',
17931     
17932     keyboardNavigation: true,
17933     
17934     calendarWeeks: false,
17935     
17936     startDate: -Infinity,
17937     
17938     endDate: Infinity,
17939     
17940     daysOfWeekDisabled: [],
17941     
17942     _events: [],
17943     
17944     singleMode : false,
17945     
17946     UTCDate: function()
17947     {
17948         return new Date(Date.UTC.apply(Date, arguments));
17949     },
17950     
17951     UTCToday: function()
17952     {
17953         var today = new Date();
17954         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17955     },
17956     
17957     getDate: function() {
17958             var d = this.getUTCDate();
17959             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17960     },
17961     
17962     getUTCDate: function() {
17963             return this.date;
17964     },
17965     
17966     setDate: function(d) {
17967             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17968     },
17969     
17970     setUTCDate: function(d) {
17971             this.date = d;
17972             this.setValue(this.formatDate(this.date));
17973     },
17974         
17975     onRender: function(ct, position)
17976     {
17977         
17978         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17979         
17980         this.language = this.language || 'en';
17981         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17982         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17983         
17984         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17985         this.format = this.format || 'm/d/y';
17986         this.isInline = false;
17987         this.isInput = true;
17988         this.component = this.el.select('.add-on', true).first() || false;
17989         this.component = (this.component && this.component.length === 0) ? false : this.component;
17990         this.hasInput = this.component && this.inputEl().length;
17991         
17992         if (typeof(this.minViewMode === 'string')) {
17993             switch (this.minViewMode) {
17994                 case 'months':
17995                     this.minViewMode = 1;
17996                     break;
17997                 case 'years':
17998                     this.minViewMode = 2;
17999                     break;
18000                 default:
18001                     this.minViewMode = 0;
18002                     break;
18003             }
18004         }
18005         
18006         if (typeof(this.viewMode === 'string')) {
18007             switch (this.viewMode) {
18008                 case 'months':
18009                     this.viewMode = 1;
18010                     break;
18011                 case 'years':
18012                     this.viewMode = 2;
18013                     break;
18014                 default:
18015                     this.viewMode = 0;
18016                     break;
18017             }
18018         }
18019                 
18020         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18021         
18022 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18023         
18024         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18025         
18026         this.picker().on('mousedown', this.onMousedown, this);
18027         this.picker().on('click', this.onClick, this);
18028         
18029         this.picker().addClass('datepicker-dropdown');
18030         
18031         this.startViewMode = this.viewMode;
18032         
18033         if(this.singleMode){
18034             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18035                 v.setVisibilityMode(Roo.Element.DISPLAY);
18036                 v.hide();
18037             });
18038             
18039             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18040                 v.setStyle('width', '189px');
18041             });
18042         }
18043         
18044         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18045             if(!this.calendarWeeks){
18046                 v.remove();
18047                 return;
18048             }
18049             
18050             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18051             v.attr('colspan', function(i, val){
18052                 return parseInt(val) + 1;
18053             });
18054         });
18055                         
18056         
18057         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18058         
18059         this.setStartDate(this.startDate);
18060         this.setEndDate(this.endDate);
18061         
18062         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18063         
18064         this.fillDow();
18065         this.fillMonths();
18066         this.update();
18067         this.showMode();
18068         
18069         if(this.isInline) {
18070             this.show();
18071         }
18072     },
18073     
18074     picker : function()
18075     {
18076         return this.pickerEl;
18077 //        return this.el.select('.datepicker', true).first();
18078     },
18079     
18080     fillDow: function()
18081     {
18082         var dowCnt = this.weekStart;
18083         
18084         var dow = {
18085             tag: 'tr',
18086             cn: [
18087                 
18088             ]
18089         };
18090         
18091         if(this.calendarWeeks){
18092             dow.cn.push({
18093                 tag: 'th',
18094                 cls: 'cw',
18095                 html: '&nbsp;'
18096             })
18097         }
18098         
18099         while (dowCnt < this.weekStart + 7) {
18100             dow.cn.push({
18101                 tag: 'th',
18102                 cls: 'dow',
18103                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18104             });
18105         }
18106         
18107         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18108     },
18109     
18110     fillMonths: function()
18111     {    
18112         var i = 0;
18113         var months = this.picker().select('>.datepicker-months td', true).first();
18114         
18115         months.dom.innerHTML = '';
18116         
18117         while (i < 12) {
18118             var month = {
18119                 tag: 'span',
18120                 cls: 'month',
18121                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18122             };
18123             
18124             months.createChild(month);
18125         }
18126         
18127     },
18128     
18129     update: function()
18130     {
18131         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;
18132         
18133         if (this.date < this.startDate) {
18134             this.viewDate = new Date(this.startDate);
18135         } else if (this.date > this.endDate) {
18136             this.viewDate = new Date(this.endDate);
18137         } else {
18138             this.viewDate = new Date(this.date);
18139         }
18140         
18141         this.fill();
18142     },
18143     
18144     fill: function() 
18145     {
18146         var d = new Date(this.viewDate),
18147                 year = d.getUTCFullYear(),
18148                 month = d.getUTCMonth(),
18149                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18150                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18151                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18152                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18153                 currentDate = this.date && this.date.valueOf(),
18154                 today = this.UTCToday();
18155         
18156         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18157         
18158 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18159         
18160 //        this.picker.select('>tfoot th.today').
18161 //                                              .text(dates[this.language].today)
18162 //                                              .toggle(this.todayBtn !== false);
18163     
18164         this.updateNavArrows();
18165         this.fillMonths();
18166                                                 
18167         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18168         
18169         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18170          
18171         prevMonth.setUTCDate(day);
18172         
18173         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18174         
18175         var nextMonth = new Date(prevMonth);
18176         
18177         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18178         
18179         nextMonth = nextMonth.valueOf();
18180         
18181         var fillMonths = false;
18182         
18183         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18184         
18185         while(prevMonth.valueOf() < nextMonth) {
18186             var clsName = '';
18187             
18188             if (prevMonth.getUTCDay() === this.weekStart) {
18189                 if(fillMonths){
18190                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18191                 }
18192                     
18193                 fillMonths = {
18194                     tag: 'tr',
18195                     cn: []
18196                 };
18197                 
18198                 if(this.calendarWeeks){
18199                     // ISO 8601: First week contains first thursday.
18200                     // ISO also states week starts on Monday, but we can be more abstract here.
18201                     var
18202                     // Start of current week: based on weekstart/current date
18203                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18204                     // Thursday of this week
18205                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18206                     // First Thursday of year, year from thursday
18207                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18208                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18209                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18210                     
18211                     fillMonths.cn.push({
18212                         tag: 'td',
18213                         cls: 'cw',
18214                         html: calWeek
18215                     });
18216                 }
18217             }
18218             
18219             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18220                 clsName += ' old';
18221             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18222                 clsName += ' new';
18223             }
18224             if (this.todayHighlight &&
18225                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18226                 prevMonth.getUTCMonth() == today.getMonth() &&
18227                 prevMonth.getUTCDate() == today.getDate()) {
18228                 clsName += ' today';
18229             }
18230             
18231             if (currentDate && prevMonth.valueOf() === currentDate) {
18232                 clsName += ' active';
18233             }
18234             
18235             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18236                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18237                     clsName += ' disabled';
18238             }
18239             
18240             fillMonths.cn.push({
18241                 tag: 'td',
18242                 cls: 'day ' + clsName,
18243                 html: prevMonth.getDate()
18244             });
18245             
18246             prevMonth.setDate(prevMonth.getDate()+1);
18247         }
18248           
18249         var currentYear = this.date && this.date.getUTCFullYear();
18250         var currentMonth = this.date && this.date.getUTCMonth();
18251         
18252         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18253         
18254         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18255             v.removeClass('active');
18256             
18257             if(currentYear === year && k === currentMonth){
18258                 v.addClass('active');
18259             }
18260             
18261             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18262                 v.addClass('disabled');
18263             }
18264             
18265         });
18266         
18267         
18268         year = parseInt(year/10, 10) * 10;
18269         
18270         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18271         
18272         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18273         
18274         year -= 1;
18275         for (var i = -1; i < 11; i++) {
18276             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18277                 tag: 'span',
18278                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18279                 html: year
18280             });
18281             
18282             year += 1;
18283         }
18284     },
18285     
18286     showMode: function(dir) 
18287     {
18288         if (dir) {
18289             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18290         }
18291         
18292         Roo.each(this.picker().select('>div',true).elements, function(v){
18293             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18294             v.hide();
18295         });
18296         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18297     },
18298     
18299     place: function()
18300     {
18301         if(this.isInline) {
18302             return;
18303         }
18304         
18305         this.picker().removeClass(['bottom', 'top']);
18306         
18307         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18308             /*
18309              * place to the top of element!
18310              *
18311              */
18312             
18313             this.picker().addClass('top');
18314             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18315             
18316             return;
18317         }
18318         
18319         this.picker().addClass('bottom');
18320         
18321         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18322     },
18323     
18324     parseDate : function(value)
18325     {
18326         if(!value || value instanceof Date){
18327             return value;
18328         }
18329         var v = Date.parseDate(value, this.format);
18330         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18331             v = Date.parseDate(value, 'Y-m-d');
18332         }
18333         if(!v && this.altFormats){
18334             if(!this.altFormatsArray){
18335                 this.altFormatsArray = this.altFormats.split("|");
18336             }
18337             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18338                 v = Date.parseDate(value, this.altFormatsArray[i]);
18339             }
18340         }
18341         return v;
18342     },
18343     
18344     formatDate : function(date, fmt)
18345     {   
18346         return (!date || !(date instanceof Date)) ?
18347         date : date.dateFormat(fmt || this.format);
18348     },
18349     
18350     onFocus : function()
18351     {
18352         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18353         this.show();
18354     },
18355     
18356     onBlur : function()
18357     {
18358         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18359         
18360         var d = this.inputEl().getValue();
18361         
18362         this.setValue(d);
18363                 
18364         this.hide();
18365     },
18366     
18367     show : function()
18368     {
18369         this.picker().show();
18370         this.update();
18371         this.place();
18372         
18373         this.fireEvent('show', this, this.date);
18374     },
18375     
18376     hide : function()
18377     {
18378         if(this.isInline) {
18379             return;
18380         }
18381         this.picker().hide();
18382         this.viewMode = this.startViewMode;
18383         this.showMode();
18384         
18385         this.fireEvent('hide', this, this.date);
18386         
18387     },
18388     
18389     onMousedown: function(e)
18390     {
18391         e.stopPropagation();
18392         e.preventDefault();
18393     },
18394     
18395     keyup: function(e)
18396     {
18397         Roo.bootstrap.DateField.superclass.keyup.call(this);
18398         this.update();
18399     },
18400
18401     setValue: function(v)
18402     {
18403         if(this.fireEvent('beforeselect', this, v) !== false){
18404             var d = new Date(this.parseDate(v) ).clearTime();
18405         
18406             if(isNaN(d.getTime())){
18407                 this.date = this.viewDate = '';
18408                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18409                 return;
18410             }
18411
18412             v = this.formatDate(d);
18413
18414             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18415
18416             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18417
18418             this.update();
18419
18420             this.fireEvent('select', this, this.date);
18421         }
18422     },
18423     
18424     getValue: function()
18425     {
18426         return this.formatDate(this.date);
18427     },
18428     
18429     fireKey: function(e)
18430     {
18431         if (!this.picker().isVisible()){
18432             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18433                 this.show();
18434             }
18435             return;
18436         }
18437         
18438         var dateChanged = false,
18439         dir, day, month,
18440         newDate, newViewDate;
18441         
18442         switch(e.keyCode){
18443             case 27: // escape
18444                 this.hide();
18445                 e.preventDefault();
18446                 break;
18447             case 37: // left
18448             case 39: // right
18449                 if (!this.keyboardNavigation) {
18450                     break;
18451                 }
18452                 dir = e.keyCode == 37 ? -1 : 1;
18453                 
18454                 if (e.ctrlKey){
18455                     newDate = this.moveYear(this.date, dir);
18456                     newViewDate = this.moveYear(this.viewDate, dir);
18457                 } else if (e.shiftKey){
18458                     newDate = this.moveMonth(this.date, dir);
18459                     newViewDate = this.moveMonth(this.viewDate, dir);
18460                 } else {
18461                     newDate = new Date(this.date);
18462                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18463                     newViewDate = new Date(this.viewDate);
18464                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18465                 }
18466                 if (this.dateWithinRange(newDate)){
18467                     this.date = newDate;
18468                     this.viewDate = newViewDate;
18469                     this.setValue(this.formatDate(this.date));
18470 //                    this.update();
18471                     e.preventDefault();
18472                     dateChanged = true;
18473                 }
18474                 break;
18475             case 38: // up
18476             case 40: // down
18477                 if (!this.keyboardNavigation) {
18478                     break;
18479                 }
18480                 dir = e.keyCode == 38 ? -1 : 1;
18481                 if (e.ctrlKey){
18482                     newDate = this.moveYear(this.date, dir);
18483                     newViewDate = this.moveYear(this.viewDate, dir);
18484                 } else if (e.shiftKey){
18485                     newDate = this.moveMonth(this.date, dir);
18486                     newViewDate = this.moveMonth(this.viewDate, dir);
18487                 } else {
18488                     newDate = new Date(this.date);
18489                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18490                     newViewDate = new Date(this.viewDate);
18491                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18492                 }
18493                 if (this.dateWithinRange(newDate)){
18494                     this.date = newDate;
18495                     this.viewDate = newViewDate;
18496                     this.setValue(this.formatDate(this.date));
18497 //                    this.update();
18498                     e.preventDefault();
18499                     dateChanged = true;
18500                 }
18501                 break;
18502             case 13: // enter
18503                 this.setValue(this.formatDate(this.date));
18504                 this.hide();
18505                 e.preventDefault();
18506                 break;
18507             case 9: // tab
18508                 this.setValue(this.formatDate(this.date));
18509                 this.hide();
18510                 break;
18511             case 16: // shift
18512             case 17: // ctrl
18513             case 18: // alt
18514                 break;
18515             default :
18516                 this.hide();
18517                 
18518         }
18519     },
18520     
18521     
18522     onClick: function(e) 
18523     {
18524         e.stopPropagation();
18525         e.preventDefault();
18526         
18527         var target = e.getTarget();
18528         
18529         if(target.nodeName.toLowerCase() === 'i'){
18530             target = Roo.get(target).dom.parentNode;
18531         }
18532         
18533         var nodeName = target.nodeName;
18534         var className = target.className;
18535         var html = target.innerHTML;
18536         //Roo.log(nodeName);
18537         
18538         switch(nodeName.toLowerCase()) {
18539             case 'th':
18540                 switch(className) {
18541                     case 'switch':
18542                         this.showMode(1);
18543                         break;
18544                     case 'prev':
18545                     case 'next':
18546                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18547                         switch(this.viewMode){
18548                                 case 0:
18549                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18550                                         break;
18551                                 case 1:
18552                                 case 2:
18553                                         this.viewDate = this.moveYear(this.viewDate, dir);
18554                                         break;
18555                         }
18556                         this.fill();
18557                         break;
18558                     case 'today':
18559                         var date = new Date();
18560                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18561 //                        this.fill()
18562                         this.setValue(this.formatDate(this.date));
18563                         
18564                         this.hide();
18565                         break;
18566                 }
18567                 break;
18568             case 'span':
18569                 if (className.indexOf('disabled') < 0) {
18570                     this.viewDate.setUTCDate(1);
18571                     if (className.indexOf('month') > -1) {
18572                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18573                     } else {
18574                         var year = parseInt(html, 10) || 0;
18575                         this.viewDate.setUTCFullYear(year);
18576                         
18577                     }
18578                     
18579                     if(this.singleMode){
18580                         this.setValue(this.formatDate(this.viewDate));
18581                         this.hide();
18582                         return;
18583                     }
18584                     
18585                     this.showMode(-1);
18586                     this.fill();
18587                 }
18588                 break;
18589                 
18590             case 'td':
18591                 //Roo.log(className);
18592                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18593                     var day = parseInt(html, 10) || 1;
18594                     var year = this.viewDate.getUTCFullYear(),
18595                         month = this.viewDate.getUTCMonth();
18596
18597                     if (className.indexOf('old') > -1) {
18598                         if(month === 0 ){
18599                             month = 11;
18600                             year -= 1;
18601                         }else{
18602                             month -= 1;
18603                         }
18604                     } else if (className.indexOf('new') > -1) {
18605                         if (month == 11) {
18606                             month = 0;
18607                             year += 1;
18608                         } else {
18609                             month += 1;
18610                         }
18611                     }
18612                     //Roo.log([year,month,day]);
18613                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18614                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18615 //                    this.fill();
18616                     //Roo.log(this.formatDate(this.date));
18617                     this.setValue(this.formatDate(this.date));
18618                     this.hide();
18619                 }
18620                 break;
18621         }
18622     },
18623     
18624     setStartDate: function(startDate)
18625     {
18626         this.startDate = startDate || -Infinity;
18627         if (this.startDate !== -Infinity) {
18628             this.startDate = this.parseDate(this.startDate);
18629         }
18630         this.update();
18631         this.updateNavArrows();
18632     },
18633
18634     setEndDate: function(endDate)
18635     {
18636         this.endDate = endDate || Infinity;
18637         if (this.endDate !== Infinity) {
18638             this.endDate = this.parseDate(this.endDate);
18639         }
18640         this.update();
18641         this.updateNavArrows();
18642     },
18643     
18644     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18645     {
18646         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18647         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18648             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18649         }
18650         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18651             return parseInt(d, 10);
18652         });
18653         this.update();
18654         this.updateNavArrows();
18655     },
18656     
18657     updateNavArrows: function() 
18658     {
18659         if(this.singleMode){
18660             return;
18661         }
18662         
18663         var d = new Date(this.viewDate),
18664         year = d.getUTCFullYear(),
18665         month = d.getUTCMonth();
18666         
18667         Roo.each(this.picker().select('.prev', true).elements, function(v){
18668             v.show();
18669             switch (this.viewMode) {
18670                 case 0:
18671
18672                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18673                         v.hide();
18674                     }
18675                     break;
18676                 case 1:
18677                 case 2:
18678                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18679                         v.hide();
18680                     }
18681                     break;
18682             }
18683         });
18684         
18685         Roo.each(this.picker().select('.next', true).elements, function(v){
18686             v.show();
18687             switch (this.viewMode) {
18688                 case 0:
18689
18690                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18691                         v.hide();
18692                     }
18693                     break;
18694                 case 1:
18695                 case 2:
18696                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18697                         v.hide();
18698                     }
18699                     break;
18700             }
18701         })
18702     },
18703     
18704     moveMonth: function(date, dir)
18705     {
18706         if (!dir) {
18707             return date;
18708         }
18709         var new_date = new Date(date.valueOf()),
18710         day = new_date.getUTCDate(),
18711         month = new_date.getUTCMonth(),
18712         mag = Math.abs(dir),
18713         new_month, test;
18714         dir = dir > 0 ? 1 : -1;
18715         if (mag == 1){
18716             test = dir == -1
18717             // If going back one month, make sure month is not current month
18718             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18719             ? function(){
18720                 return new_date.getUTCMonth() == month;
18721             }
18722             // If going forward one month, make sure month is as expected
18723             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18724             : function(){
18725                 return new_date.getUTCMonth() != new_month;
18726             };
18727             new_month = month + dir;
18728             new_date.setUTCMonth(new_month);
18729             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18730             if (new_month < 0 || new_month > 11) {
18731                 new_month = (new_month + 12) % 12;
18732             }
18733         } else {
18734             // For magnitudes >1, move one month at a time...
18735             for (var i=0; i<mag; i++) {
18736                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18737                 new_date = this.moveMonth(new_date, dir);
18738             }
18739             // ...then reset the day, keeping it in the new month
18740             new_month = new_date.getUTCMonth();
18741             new_date.setUTCDate(day);
18742             test = function(){
18743                 return new_month != new_date.getUTCMonth();
18744             };
18745         }
18746         // Common date-resetting loop -- if date is beyond end of month, make it
18747         // end of month
18748         while (test()){
18749             new_date.setUTCDate(--day);
18750             new_date.setUTCMonth(new_month);
18751         }
18752         return new_date;
18753     },
18754
18755     moveYear: function(date, dir)
18756     {
18757         return this.moveMonth(date, dir*12);
18758     },
18759
18760     dateWithinRange: function(date)
18761     {
18762         return date >= this.startDate && date <= this.endDate;
18763     },
18764
18765     
18766     remove: function() 
18767     {
18768         this.picker().remove();
18769     },
18770     
18771     validateValue : function(value)
18772     {
18773         if(value.length < 1)  {
18774             if(this.allowBlank){
18775                 return true;
18776             }
18777             return false;
18778         }
18779         
18780         if(value.length < this.minLength){
18781             return false;
18782         }
18783         if(value.length > this.maxLength){
18784             return false;
18785         }
18786         if(this.vtype){
18787             var vt = Roo.form.VTypes;
18788             if(!vt[this.vtype](value, this)){
18789                 return false;
18790             }
18791         }
18792         if(typeof this.validator == "function"){
18793             var msg = this.validator(value);
18794             if(msg !== true){
18795                 return false;
18796             }
18797         }
18798         
18799         if(this.regex && !this.regex.test(value)){
18800             return false;
18801         }
18802         
18803         if(typeof(this.parseDate(value)) == 'undefined'){
18804             return false;
18805         }
18806         
18807         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18808             return false;
18809         }      
18810         
18811         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18812             return false;
18813         } 
18814         
18815         
18816         return true;
18817     }
18818    
18819 });
18820
18821 Roo.apply(Roo.bootstrap.DateField,  {
18822     
18823     head : {
18824         tag: 'thead',
18825         cn: [
18826         {
18827             tag: 'tr',
18828             cn: [
18829             {
18830                 tag: 'th',
18831                 cls: 'prev',
18832                 html: '<i class="fa fa-arrow-left"/>'
18833             },
18834             {
18835                 tag: 'th',
18836                 cls: 'switch',
18837                 colspan: '5'
18838             },
18839             {
18840                 tag: 'th',
18841                 cls: 'next',
18842                 html: '<i class="fa fa-arrow-right"/>'
18843             }
18844
18845             ]
18846         }
18847         ]
18848     },
18849     
18850     content : {
18851         tag: 'tbody',
18852         cn: [
18853         {
18854             tag: 'tr',
18855             cn: [
18856             {
18857                 tag: 'td',
18858                 colspan: '7'
18859             }
18860             ]
18861         }
18862         ]
18863     },
18864     
18865     footer : {
18866         tag: 'tfoot',
18867         cn: [
18868         {
18869             tag: 'tr',
18870             cn: [
18871             {
18872                 tag: 'th',
18873                 colspan: '7',
18874                 cls: 'today'
18875             }
18876                     
18877             ]
18878         }
18879         ]
18880     },
18881     
18882     dates:{
18883         en: {
18884             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18885             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18886             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18887             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18888             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18889             today: "Today"
18890         }
18891     },
18892     
18893     modes: [
18894     {
18895         clsName: 'days',
18896         navFnc: 'Month',
18897         navStep: 1
18898     },
18899     {
18900         clsName: 'months',
18901         navFnc: 'FullYear',
18902         navStep: 1
18903     },
18904     {
18905         clsName: 'years',
18906         navFnc: 'FullYear',
18907         navStep: 10
18908     }]
18909 });
18910
18911 Roo.apply(Roo.bootstrap.DateField,  {
18912   
18913     template : {
18914         tag: 'div',
18915         cls: 'datepicker dropdown-menu roo-dynamic',
18916         cn: [
18917         {
18918             tag: 'div',
18919             cls: 'datepicker-days',
18920             cn: [
18921             {
18922                 tag: 'table',
18923                 cls: 'table-condensed',
18924                 cn:[
18925                 Roo.bootstrap.DateField.head,
18926                 {
18927                     tag: 'tbody'
18928                 },
18929                 Roo.bootstrap.DateField.footer
18930                 ]
18931             }
18932             ]
18933         },
18934         {
18935             tag: 'div',
18936             cls: 'datepicker-months',
18937             cn: [
18938             {
18939                 tag: 'table',
18940                 cls: 'table-condensed',
18941                 cn:[
18942                 Roo.bootstrap.DateField.head,
18943                 Roo.bootstrap.DateField.content,
18944                 Roo.bootstrap.DateField.footer
18945                 ]
18946             }
18947             ]
18948         },
18949         {
18950             tag: 'div',
18951             cls: 'datepicker-years',
18952             cn: [
18953             {
18954                 tag: 'table',
18955                 cls: 'table-condensed',
18956                 cn:[
18957                 Roo.bootstrap.DateField.head,
18958                 Roo.bootstrap.DateField.content,
18959                 Roo.bootstrap.DateField.footer
18960                 ]
18961             }
18962             ]
18963         }
18964         ]
18965     }
18966 });
18967
18968  
18969
18970  /*
18971  * - LGPL
18972  *
18973  * TimeField
18974  * 
18975  */
18976
18977 /**
18978  * @class Roo.bootstrap.TimeField
18979  * @extends Roo.bootstrap.Input
18980  * Bootstrap DateField class
18981  * 
18982  * 
18983  * @constructor
18984  * Create a new TimeField
18985  * @param {Object} config The config object
18986  */
18987
18988 Roo.bootstrap.TimeField = function(config){
18989     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18990     this.addEvents({
18991             /**
18992              * @event show
18993              * Fires when this field show.
18994              * @param {Roo.bootstrap.DateField} thisthis
18995              * @param {Mixed} date The date value
18996              */
18997             show : true,
18998             /**
18999              * @event show
19000              * Fires when this field hide.
19001              * @param {Roo.bootstrap.DateField} this
19002              * @param {Mixed} date The date value
19003              */
19004             hide : true,
19005             /**
19006              * @event select
19007              * Fires when select a date.
19008              * @param {Roo.bootstrap.DateField} this
19009              * @param {Mixed} date The date value
19010              */
19011             select : true
19012         });
19013 };
19014
19015 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19016     
19017     /**
19018      * @cfg {String} format
19019      * The default time format string which can be overriden for localization support.  The format must be
19020      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19021      */
19022     format : "H:i",
19023        
19024     onRender: function(ct, position)
19025     {
19026         
19027         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19028                 
19029         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19030         
19031         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19032         
19033         this.pop = this.picker().select('>.datepicker-time',true).first();
19034         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19035         
19036         this.picker().on('mousedown', this.onMousedown, this);
19037         this.picker().on('click', this.onClick, this);
19038         
19039         this.picker().addClass('datepicker-dropdown');
19040     
19041         this.fillTime();
19042         this.update();
19043             
19044         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19045         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19046         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19047         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19048         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19049         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19050
19051     },
19052     
19053     fireKey: function(e){
19054         if (!this.picker().isVisible()){
19055             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19056                 this.show();
19057             }
19058             return;
19059         }
19060
19061         e.preventDefault();
19062         
19063         switch(e.keyCode){
19064             case 27: // escape
19065                 this.hide();
19066                 break;
19067             case 37: // left
19068             case 39: // right
19069                 this.onTogglePeriod();
19070                 break;
19071             case 38: // up
19072                 this.onIncrementMinutes();
19073                 break;
19074             case 40: // down
19075                 this.onDecrementMinutes();
19076                 break;
19077             case 13: // enter
19078             case 9: // tab
19079                 this.setTime();
19080                 break;
19081         }
19082     },
19083     
19084     onClick: function(e) {
19085         e.stopPropagation();
19086         e.preventDefault();
19087     },
19088     
19089     picker : function()
19090     {
19091         return this.el.select('.datepicker', true).first();
19092     },
19093     
19094     fillTime: function()
19095     {    
19096         var time = this.pop.select('tbody', true).first();
19097         
19098         time.dom.innerHTML = '';
19099         
19100         time.createChild({
19101             tag: 'tr',
19102             cn: [
19103                 {
19104                     tag: 'td',
19105                     cn: [
19106                         {
19107                             tag: 'a',
19108                             href: '#',
19109                             cls: 'btn',
19110                             cn: [
19111                                 {
19112                                     tag: 'span',
19113                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19114                                 }
19115                             ]
19116                         } 
19117                     ]
19118                 },
19119                 {
19120                     tag: 'td',
19121                     cls: 'separator'
19122                 },
19123                 {
19124                     tag: 'td',
19125                     cn: [
19126                         {
19127                             tag: 'a',
19128                             href: '#',
19129                             cls: 'btn',
19130                             cn: [
19131                                 {
19132                                     tag: 'span',
19133                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19134                                 }
19135                             ]
19136                         }
19137                     ]
19138                 },
19139                 {
19140                     tag: 'td',
19141                     cls: 'separator'
19142                 }
19143             ]
19144         });
19145         
19146         time.createChild({
19147             tag: 'tr',
19148             cn: [
19149                 {
19150                     tag: 'td',
19151                     cn: [
19152                         {
19153                             tag: 'span',
19154                             cls: 'timepicker-hour',
19155                             html: '00'
19156                         }  
19157                     ]
19158                 },
19159                 {
19160                     tag: 'td',
19161                     cls: 'separator',
19162                     html: ':'
19163                 },
19164                 {
19165                     tag: 'td',
19166                     cn: [
19167                         {
19168                             tag: 'span',
19169                             cls: 'timepicker-minute',
19170                             html: '00'
19171                         }  
19172                     ]
19173                 },
19174                 {
19175                     tag: 'td',
19176                     cls: 'separator'
19177                 },
19178                 {
19179                     tag: 'td',
19180                     cn: [
19181                         {
19182                             tag: 'button',
19183                             type: 'button',
19184                             cls: 'btn btn-primary period',
19185                             html: 'AM'
19186                             
19187                         }
19188                     ]
19189                 }
19190             ]
19191         });
19192         
19193         time.createChild({
19194             tag: 'tr',
19195             cn: [
19196                 {
19197                     tag: 'td',
19198                     cn: [
19199                         {
19200                             tag: 'a',
19201                             href: '#',
19202                             cls: 'btn',
19203                             cn: [
19204                                 {
19205                                     tag: 'span',
19206                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19207                                 }
19208                             ]
19209                         }
19210                     ]
19211                 },
19212                 {
19213                     tag: 'td',
19214                     cls: 'separator'
19215                 },
19216                 {
19217                     tag: 'td',
19218                     cn: [
19219                         {
19220                             tag: 'a',
19221                             href: '#',
19222                             cls: 'btn',
19223                             cn: [
19224                                 {
19225                                     tag: 'span',
19226                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19227                                 }
19228                             ]
19229                         }
19230                     ]
19231                 },
19232                 {
19233                     tag: 'td',
19234                     cls: 'separator'
19235                 }
19236             ]
19237         });
19238         
19239     },
19240     
19241     update: function()
19242     {
19243         
19244         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19245         
19246         this.fill();
19247     },
19248     
19249     fill: function() 
19250     {
19251         var hours = this.time.getHours();
19252         var minutes = this.time.getMinutes();
19253         var period = 'AM';
19254         
19255         if(hours > 11){
19256             period = 'PM';
19257         }
19258         
19259         if(hours == 0){
19260             hours = 12;
19261         }
19262         
19263         
19264         if(hours > 12){
19265             hours = hours - 12;
19266         }
19267         
19268         if(hours < 10){
19269             hours = '0' + hours;
19270         }
19271         
19272         if(minutes < 10){
19273             minutes = '0' + minutes;
19274         }
19275         
19276         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19277         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19278         this.pop.select('button', true).first().dom.innerHTML = period;
19279         
19280     },
19281     
19282     place: function()
19283     {   
19284         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19285         
19286         var cls = ['bottom'];
19287         
19288         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19289             cls.pop();
19290             cls.push('top');
19291         }
19292         
19293         cls.push('right');
19294         
19295         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19296             cls.pop();
19297             cls.push('left');
19298         }
19299         
19300         this.picker().addClass(cls.join('-'));
19301         
19302         var _this = this;
19303         
19304         Roo.each(cls, function(c){
19305             if(c == 'bottom'){
19306                 _this.picker().setTop(_this.inputEl().getHeight());
19307                 return;
19308             }
19309             if(c == 'top'){
19310                 _this.picker().setTop(0 - _this.picker().getHeight());
19311                 return;
19312             }
19313             
19314             if(c == 'left'){
19315                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19316                 return;
19317             }
19318             if(c == 'right'){
19319                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19320                 return;
19321             }
19322         });
19323         
19324     },
19325   
19326     onFocus : function()
19327     {
19328         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19329         this.show();
19330     },
19331     
19332     onBlur : function()
19333     {
19334         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19335         this.hide();
19336     },
19337     
19338     show : function()
19339     {
19340         this.picker().show();
19341         this.pop.show();
19342         this.update();
19343         this.place();
19344         
19345         this.fireEvent('show', this, this.date);
19346     },
19347     
19348     hide : function()
19349     {
19350         this.picker().hide();
19351         this.pop.hide();
19352         
19353         this.fireEvent('hide', this, this.date);
19354     },
19355     
19356     setTime : function()
19357     {
19358         this.hide();
19359         this.setValue(this.time.format(this.format));
19360         
19361         this.fireEvent('select', this, this.date);
19362         
19363         
19364     },
19365     
19366     onMousedown: function(e){
19367         e.stopPropagation();
19368         e.preventDefault();
19369     },
19370     
19371     onIncrementHours: function()
19372     {
19373         Roo.log('onIncrementHours');
19374         this.time = this.time.add(Date.HOUR, 1);
19375         this.update();
19376         
19377     },
19378     
19379     onDecrementHours: function()
19380     {
19381         Roo.log('onDecrementHours');
19382         this.time = this.time.add(Date.HOUR, -1);
19383         this.update();
19384     },
19385     
19386     onIncrementMinutes: function()
19387     {
19388         Roo.log('onIncrementMinutes');
19389         this.time = this.time.add(Date.MINUTE, 1);
19390         this.update();
19391     },
19392     
19393     onDecrementMinutes: function()
19394     {
19395         Roo.log('onDecrementMinutes');
19396         this.time = this.time.add(Date.MINUTE, -1);
19397         this.update();
19398     },
19399     
19400     onTogglePeriod: function()
19401     {
19402         Roo.log('onTogglePeriod');
19403         this.time = this.time.add(Date.HOUR, 12);
19404         this.update();
19405     }
19406     
19407    
19408 });
19409
19410 Roo.apply(Roo.bootstrap.TimeField,  {
19411     
19412     content : {
19413         tag: 'tbody',
19414         cn: [
19415             {
19416                 tag: 'tr',
19417                 cn: [
19418                 {
19419                     tag: 'td',
19420                     colspan: '7'
19421                 }
19422                 ]
19423             }
19424         ]
19425     },
19426     
19427     footer : {
19428         tag: 'tfoot',
19429         cn: [
19430             {
19431                 tag: 'tr',
19432                 cn: [
19433                 {
19434                     tag: 'th',
19435                     colspan: '7',
19436                     cls: '',
19437                     cn: [
19438                         {
19439                             tag: 'button',
19440                             cls: 'btn btn-info ok',
19441                             html: 'OK'
19442                         }
19443                     ]
19444                 }
19445
19446                 ]
19447             }
19448         ]
19449     }
19450 });
19451
19452 Roo.apply(Roo.bootstrap.TimeField,  {
19453   
19454     template : {
19455         tag: 'div',
19456         cls: 'datepicker dropdown-menu',
19457         cn: [
19458             {
19459                 tag: 'div',
19460                 cls: 'datepicker-time',
19461                 cn: [
19462                 {
19463                     tag: 'table',
19464                     cls: 'table-condensed',
19465                     cn:[
19466                     Roo.bootstrap.TimeField.content,
19467                     Roo.bootstrap.TimeField.footer
19468                     ]
19469                 }
19470                 ]
19471             }
19472         ]
19473     }
19474 });
19475
19476  
19477
19478  /*
19479  * - LGPL
19480  *
19481  * MonthField
19482  * 
19483  */
19484
19485 /**
19486  * @class Roo.bootstrap.MonthField
19487  * @extends Roo.bootstrap.Input
19488  * Bootstrap MonthField class
19489  * 
19490  * @cfg {String} language default en
19491  * 
19492  * @constructor
19493  * Create a new MonthField
19494  * @param {Object} config The config object
19495  */
19496
19497 Roo.bootstrap.MonthField = function(config){
19498     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19499     
19500     this.addEvents({
19501         /**
19502          * @event show
19503          * Fires when this field show.
19504          * @param {Roo.bootstrap.MonthField} this
19505          * @param {Mixed} date The date value
19506          */
19507         show : true,
19508         /**
19509          * @event show
19510          * Fires when this field hide.
19511          * @param {Roo.bootstrap.MonthField} this
19512          * @param {Mixed} date The date value
19513          */
19514         hide : true,
19515         /**
19516          * @event select
19517          * Fires when select a date.
19518          * @param {Roo.bootstrap.MonthField} this
19519          * @param {String} oldvalue The old value
19520          * @param {String} newvalue The new value
19521          */
19522         select : true
19523     });
19524 };
19525
19526 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19527     
19528     onRender: function(ct, position)
19529     {
19530         
19531         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19532         
19533         this.language = this.language || 'en';
19534         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19535         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19536         
19537         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19538         this.isInline = false;
19539         this.isInput = true;
19540         this.component = this.el.select('.add-on', true).first() || false;
19541         this.component = (this.component && this.component.length === 0) ? false : this.component;
19542         this.hasInput = this.component && this.inputEL().length;
19543         
19544         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19545         
19546         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19547         
19548         this.picker().on('mousedown', this.onMousedown, this);
19549         this.picker().on('click', this.onClick, this);
19550         
19551         this.picker().addClass('datepicker-dropdown');
19552         
19553         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19554             v.setStyle('width', '189px');
19555         });
19556         
19557         this.fillMonths();
19558         
19559         this.update();
19560         
19561         if(this.isInline) {
19562             this.show();
19563         }
19564         
19565     },
19566     
19567     setValue: function(v, suppressEvent)
19568     {   
19569         var o = this.getValue();
19570         
19571         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19572         
19573         this.update();
19574
19575         if(suppressEvent !== true){
19576             this.fireEvent('select', this, o, v);
19577         }
19578         
19579     },
19580     
19581     getValue: function()
19582     {
19583         return this.value;
19584     },
19585     
19586     onClick: function(e) 
19587     {
19588         e.stopPropagation();
19589         e.preventDefault();
19590         
19591         var target = e.getTarget();
19592         
19593         if(target.nodeName.toLowerCase() === 'i'){
19594             target = Roo.get(target).dom.parentNode;
19595         }
19596         
19597         var nodeName = target.nodeName;
19598         var className = target.className;
19599         var html = target.innerHTML;
19600         
19601         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19602             return;
19603         }
19604         
19605         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19606         
19607         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19608         
19609         this.hide();
19610                         
19611     },
19612     
19613     picker : function()
19614     {
19615         return this.pickerEl;
19616     },
19617     
19618     fillMonths: function()
19619     {    
19620         var i = 0;
19621         var months = this.picker().select('>.datepicker-months td', true).first();
19622         
19623         months.dom.innerHTML = '';
19624         
19625         while (i < 12) {
19626             var month = {
19627                 tag: 'span',
19628                 cls: 'month',
19629                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19630             };
19631             
19632             months.createChild(month);
19633         }
19634         
19635     },
19636     
19637     update: function()
19638     {
19639         var _this = this;
19640         
19641         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19642             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19643         }
19644         
19645         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19646             e.removeClass('active');
19647             
19648             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19649                 e.addClass('active');
19650             }
19651         })
19652     },
19653     
19654     place: function()
19655     {
19656         if(this.isInline) {
19657             return;
19658         }
19659         
19660         this.picker().removeClass(['bottom', 'top']);
19661         
19662         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19663             /*
19664              * place to the top of element!
19665              *
19666              */
19667             
19668             this.picker().addClass('top');
19669             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19670             
19671             return;
19672         }
19673         
19674         this.picker().addClass('bottom');
19675         
19676         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19677     },
19678     
19679     onFocus : function()
19680     {
19681         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19682         this.show();
19683     },
19684     
19685     onBlur : function()
19686     {
19687         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19688         
19689         var d = this.inputEl().getValue();
19690         
19691         this.setValue(d);
19692                 
19693         this.hide();
19694     },
19695     
19696     show : function()
19697     {
19698         this.picker().show();
19699         this.picker().select('>.datepicker-months', true).first().show();
19700         this.update();
19701         this.place();
19702         
19703         this.fireEvent('show', this, this.date);
19704     },
19705     
19706     hide : function()
19707     {
19708         if(this.isInline) {
19709             return;
19710         }
19711         this.picker().hide();
19712         this.fireEvent('hide', this, this.date);
19713         
19714     },
19715     
19716     onMousedown: function(e)
19717     {
19718         e.stopPropagation();
19719         e.preventDefault();
19720     },
19721     
19722     keyup: function(e)
19723     {
19724         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19725         this.update();
19726     },
19727
19728     fireKey: function(e)
19729     {
19730         if (!this.picker().isVisible()){
19731             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19732                 this.show();
19733             }
19734             return;
19735         }
19736         
19737         var dir;
19738         
19739         switch(e.keyCode){
19740             case 27: // escape
19741                 this.hide();
19742                 e.preventDefault();
19743                 break;
19744             case 37: // left
19745             case 39: // right
19746                 dir = e.keyCode == 37 ? -1 : 1;
19747                 
19748                 this.vIndex = this.vIndex + dir;
19749                 
19750                 if(this.vIndex < 0){
19751                     this.vIndex = 0;
19752                 }
19753                 
19754                 if(this.vIndex > 11){
19755                     this.vIndex = 11;
19756                 }
19757                 
19758                 if(isNaN(this.vIndex)){
19759                     this.vIndex = 0;
19760                 }
19761                 
19762                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19763                 
19764                 break;
19765             case 38: // up
19766             case 40: // down
19767                 
19768                 dir = e.keyCode == 38 ? -1 : 1;
19769                 
19770                 this.vIndex = this.vIndex + dir * 4;
19771                 
19772                 if(this.vIndex < 0){
19773                     this.vIndex = 0;
19774                 }
19775                 
19776                 if(this.vIndex > 11){
19777                     this.vIndex = 11;
19778                 }
19779                 
19780                 if(isNaN(this.vIndex)){
19781                     this.vIndex = 0;
19782                 }
19783                 
19784                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19785                 break;
19786                 
19787             case 13: // enter
19788                 
19789                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19790                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19791                 }
19792                 
19793                 this.hide();
19794                 e.preventDefault();
19795                 break;
19796             case 9: // tab
19797                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19798                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19799                 }
19800                 this.hide();
19801                 break;
19802             case 16: // shift
19803             case 17: // ctrl
19804             case 18: // alt
19805                 break;
19806             default :
19807                 this.hide();
19808                 
19809         }
19810     },
19811     
19812     remove: function() 
19813     {
19814         this.picker().remove();
19815     }
19816    
19817 });
19818
19819 Roo.apply(Roo.bootstrap.MonthField,  {
19820     
19821     content : {
19822         tag: 'tbody',
19823         cn: [
19824         {
19825             tag: 'tr',
19826             cn: [
19827             {
19828                 tag: 'td',
19829                 colspan: '7'
19830             }
19831             ]
19832         }
19833         ]
19834     },
19835     
19836     dates:{
19837         en: {
19838             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19839             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19840         }
19841     }
19842 });
19843
19844 Roo.apply(Roo.bootstrap.MonthField,  {
19845   
19846     template : {
19847         tag: 'div',
19848         cls: 'datepicker dropdown-menu roo-dynamic',
19849         cn: [
19850             {
19851                 tag: 'div',
19852                 cls: 'datepicker-months',
19853                 cn: [
19854                 {
19855                     tag: 'table',
19856                     cls: 'table-condensed',
19857                     cn:[
19858                         Roo.bootstrap.DateField.content
19859                     ]
19860                 }
19861                 ]
19862             }
19863         ]
19864     }
19865 });
19866
19867  
19868
19869  
19870  /*
19871  * - LGPL
19872  *
19873  * CheckBox
19874  * 
19875  */
19876
19877 /**
19878  * @class Roo.bootstrap.CheckBox
19879  * @extends Roo.bootstrap.Input
19880  * Bootstrap CheckBox class
19881  * 
19882  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19883  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19884  * @cfg {String} boxLabel The text that appears beside the checkbox
19885  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19886  * @cfg {Boolean} checked initnal the element
19887  * @cfg {Boolean} inline inline the element (default false)
19888  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19889  * 
19890  * @constructor
19891  * Create a new CheckBox
19892  * @param {Object} config The config object
19893  */
19894
19895 Roo.bootstrap.CheckBox = function(config){
19896     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19897    
19898     this.addEvents({
19899         /**
19900         * @event check
19901         * Fires when the element is checked or unchecked.
19902         * @param {Roo.bootstrap.CheckBox} this This input
19903         * @param {Boolean} checked The new checked value
19904         */
19905        check : true
19906     });
19907     
19908 };
19909
19910 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
19911   
19912     inputType: 'checkbox',
19913     inputValue: 1,
19914     valueOff: 0,
19915     boxLabel: false,
19916     checked: false,
19917     weight : false,
19918     inline: false,
19919     
19920     getAutoCreate : function()
19921     {
19922         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19923         
19924         var id = Roo.id();
19925         
19926         var cfg = {};
19927         
19928         cfg.cls = 'form-group ' + this.inputType; //input-group
19929         
19930         if(this.inline){
19931             cfg.cls += ' ' + this.inputType + '-inline';
19932         }
19933         
19934         var input =  {
19935             tag: 'input',
19936             id : id,
19937             type : this.inputType,
19938             value : this.inputValue,
19939             cls : 'roo-' + this.inputType, //'form-box',
19940             placeholder : this.placeholder || ''
19941             
19942         };
19943         
19944         if(this.inputType != 'radio'){
19945             var hidden =  {
19946                 tag: 'input',
19947                 type : 'hidden',
19948                 cls : 'roo-hidden-value',
19949                 value : this.checked ? this.valueOff : this.inputValue
19950             };
19951         }
19952         
19953             
19954         if (this.weight) { // Validity check?
19955             cfg.cls += " " + this.inputType + "-" + this.weight;
19956         }
19957         
19958         if (this.disabled) {
19959             input.disabled=true;
19960         }
19961         
19962         if(this.checked){
19963             input.checked = this.checked;
19964             
19965         }
19966         
19967         
19968         if (this.name) {
19969             
19970             input.name = this.name;
19971             
19972             if(this.inputType != 'radio'){
19973                 hidden.name = this.name;
19974                 input.name = '_hidden_' + this.name;
19975             }
19976         }
19977         
19978         if (this.size) {
19979             input.cls += ' input-' + this.size;
19980         }
19981         
19982         var settings=this;
19983         
19984         ['xs','sm','md','lg'].map(function(size){
19985             if (settings[size]) {
19986                 cfg.cls += ' col-' + size + '-' + settings[size];
19987             }
19988         });
19989         
19990         var inputblock = input;
19991          
19992         if (this.before || this.after) {
19993             
19994             inputblock = {
19995                 cls : 'input-group',
19996                 cn :  [] 
19997             };
19998             
19999             if (this.before) {
20000                 inputblock.cn.push({
20001                     tag :'span',
20002                     cls : 'input-group-addon',
20003                     html : this.before
20004                 });
20005             }
20006             
20007             inputblock.cn.push(input);
20008             
20009             if(this.inputType != 'radio'){
20010                 inputblock.cn.push(hidden);
20011             }
20012             
20013             if (this.after) {
20014                 inputblock.cn.push({
20015                     tag :'span',
20016                     cls : 'input-group-addon',
20017                     html : this.after
20018                 });
20019             }
20020             
20021         }
20022         
20023         if (align ==='left' && this.fieldLabel.length) {
20024 //                Roo.log("left and has label");
20025             cfg.cn = [
20026                 {
20027                     tag: 'label',
20028                     'for' :  id,
20029                     cls : 'control-label',
20030                     html : this.fieldLabel
20031
20032                 },
20033                 {
20034                     cls : "", 
20035                     cn: [
20036                         inputblock
20037                     ]
20038                 }
20039             ];
20040             
20041             if(this.labelWidth > 12){
20042                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20043             }
20044             
20045             if(this.labelWidth < 13 && this.labelmd == 0){
20046                 this.labelmd = this.labelWidth;
20047             }
20048             
20049             if(this.labellg > 0){
20050                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20051                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20052             }
20053             
20054             if(this.labelmd > 0){
20055                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20056                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20057             }
20058             
20059             if(this.labelsm > 0){
20060                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20061                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20062             }
20063             
20064             if(this.labelxs > 0){
20065                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20066                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20067             }
20068             
20069         } else if ( this.fieldLabel.length) {
20070 //                Roo.log(" label");
20071                 cfg.cn = [
20072                    
20073                     {
20074                         tag: this.boxLabel ? 'span' : 'label',
20075                         'for': id,
20076                         cls: 'control-label box-input-label',
20077                         //cls : 'input-group-addon',
20078                         html : this.fieldLabel
20079                         
20080                     },
20081                     
20082                     inputblock
20083                     
20084                 ];
20085
20086         } else {
20087             
20088 //                Roo.log(" no label && no align");
20089                 cfg.cn = [  inputblock ] ;
20090                 
20091                 
20092         }
20093         
20094         if(this.boxLabel){
20095              var boxLabelCfg = {
20096                 tag: 'label',
20097                 //'for': id, // box label is handled by onclick - so no for...
20098                 cls: 'box-label',
20099                 html: this.boxLabel
20100             };
20101             
20102             if(this.tooltip){
20103                 boxLabelCfg.tooltip = this.tooltip;
20104             }
20105              
20106             cfg.cn.push(boxLabelCfg);
20107         }
20108         
20109         if(this.inputType != 'radio'){
20110             cfg.cn.push(hidden);
20111         }
20112         
20113         return cfg;
20114         
20115     },
20116     
20117     /**
20118      * return the real input element.
20119      */
20120     inputEl: function ()
20121     {
20122         return this.el.select('input.roo-' + this.inputType,true).first();
20123     },
20124     hiddenEl: function ()
20125     {
20126         return this.el.select('input.roo-hidden-value',true).first();
20127     },
20128     
20129     labelEl: function()
20130     {
20131         return this.el.select('label.control-label',true).first();
20132     },
20133     /* depricated... */
20134     
20135     label: function()
20136     {
20137         return this.labelEl();
20138     },
20139     
20140     boxLabelEl: function()
20141     {
20142         return this.el.select('label.box-label',true).first();
20143     },
20144     
20145     initEvents : function()
20146     {
20147 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20148         
20149         this.inputEl().on('click', this.onClick,  this);
20150         
20151         if (this.boxLabel) { 
20152             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20153         }
20154         
20155         this.startValue = this.getValue();
20156         
20157         if(this.groupId){
20158             Roo.bootstrap.CheckBox.register(this);
20159         }
20160     },
20161     
20162     onClick : function()
20163     {   
20164         this.setChecked(!this.checked);
20165     },
20166     
20167     setChecked : function(state,suppressEvent)
20168     {
20169         this.startValue = this.getValue();
20170
20171         if(this.inputType == 'radio'){
20172             
20173             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20174                 e.dom.checked = false;
20175             });
20176             
20177             this.inputEl().dom.checked = true;
20178             
20179             this.inputEl().dom.value = this.inputValue;
20180             
20181             if(suppressEvent !== true){
20182                 this.fireEvent('check', this, true);
20183             }
20184             
20185             this.validate();
20186             
20187             return;
20188         }
20189         
20190         this.checked = state;
20191         
20192         this.inputEl().dom.checked = state;
20193         
20194         
20195         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20196         
20197         if(suppressEvent !== true){
20198             this.fireEvent('check', this, state);
20199         }
20200         
20201         this.validate();
20202     },
20203     
20204     getValue : function()
20205     {
20206         if(this.inputType == 'radio'){
20207             return this.getGroupValue();
20208         }
20209         
20210         return this.hiddenEl().dom.value;
20211         
20212     },
20213     
20214     getGroupValue : function()
20215     {
20216         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20217             return '';
20218         }
20219         
20220         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20221     },
20222     
20223     setValue : function(v,suppressEvent)
20224     {
20225         if(this.inputType == 'radio'){
20226             this.setGroupValue(v, suppressEvent);
20227             return;
20228         }
20229         
20230         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20231         
20232         this.validate();
20233     },
20234     
20235     setGroupValue : function(v, suppressEvent)
20236     {
20237         this.startValue = this.getValue();
20238         
20239         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20240             e.dom.checked = false;
20241             
20242             if(e.dom.value == v){
20243                 e.dom.checked = true;
20244             }
20245         });
20246         
20247         if(suppressEvent !== true){
20248             this.fireEvent('check', this, true);
20249         }
20250
20251         this.validate();
20252         
20253         return;
20254     },
20255     
20256     validate : function()
20257     {
20258         if(
20259                 this.disabled || 
20260                 (this.inputType == 'radio' && this.validateRadio()) ||
20261                 (this.inputType == 'checkbox' && this.validateCheckbox())
20262         ){
20263             this.markValid();
20264             return true;
20265         }
20266         
20267         this.markInvalid();
20268         return false;
20269     },
20270     
20271     validateRadio : function()
20272     {
20273         if(this.allowBlank){
20274             return true;
20275         }
20276         
20277         var valid = false;
20278         
20279         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20280             if(!e.dom.checked){
20281                 return;
20282             }
20283             
20284             valid = true;
20285             
20286             return false;
20287         });
20288         
20289         return valid;
20290     },
20291     
20292     validateCheckbox : function()
20293     {
20294         if(!this.groupId){
20295             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20296         }
20297         
20298         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20299         
20300         if(!group){
20301             return false;
20302         }
20303         
20304         var r = false;
20305         
20306         for(var i in group){
20307             if(r){
20308                 break;
20309             }
20310             
20311             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20312         }
20313         
20314         return r;
20315     },
20316     
20317     /**
20318      * Mark this field as valid
20319      */
20320     markValid : function()
20321     {
20322         var _this = this;
20323         
20324         this.fireEvent('valid', this);
20325         
20326         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20327         
20328         if(this.groupId){
20329             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20330         }
20331         
20332         if(label){
20333             label.markValid();
20334         }
20335
20336         if(this.inputType == 'radio'){
20337             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20338                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20339                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20340             });
20341             
20342             return;
20343         }
20344         
20345         if(!this.groupId){
20346             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20347             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20348             return;
20349         }
20350         
20351         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20352             
20353         if(!group){
20354             return;
20355         }
20356         
20357         for(var i in group){
20358             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20359             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20360         }
20361     },
20362     
20363      /**
20364      * Mark this field as invalid
20365      * @param {String} msg The validation message
20366      */
20367     markInvalid : function(msg)
20368     {
20369         if(this.allowBlank){
20370             return;
20371         }
20372         
20373         var _this = this;
20374         
20375         this.fireEvent('invalid', this, msg);
20376         
20377         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20378         
20379         if(this.groupId){
20380             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20381         }
20382         
20383         if(label){
20384             label.markInvalid();
20385         }
20386             
20387         if(this.inputType == 'radio'){
20388             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20389                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20390                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20391             });
20392             
20393             return;
20394         }
20395         
20396         if(!this.groupId){
20397             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20398             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20399             return;
20400         }
20401         
20402         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20403         
20404         if(!group){
20405             return;
20406         }
20407         
20408         for(var i in group){
20409             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20410             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20411         }
20412         
20413     },
20414     
20415     clearInvalid : function()
20416     {
20417         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20418         
20419         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20420         
20421         if (label) {
20422             label.iconEl.removeClass(label.validClass);
20423             label.iconEl.removeClass(label.invalidClass);
20424         }
20425     },
20426     
20427     disable : function()
20428     {
20429         if(this.inputType != 'radio'){
20430             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20431             return;
20432         }
20433         
20434         var _this = this;
20435         
20436         if(this.rendered){
20437             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20438                 _this.getActionEl().addClass(this.disabledClass);
20439                 e.dom.disabled = true;
20440             });
20441         }
20442         
20443         this.disabled = true;
20444         this.fireEvent("disable", this);
20445         return this;
20446     },
20447
20448     enable : function()
20449     {
20450         if(this.inputType != 'radio'){
20451             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20452             return;
20453         }
20454         
20455         var _this = this;
20456         
20457         if(this.rendered){
20458             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20459                 _this.getActionEl().removeClass(this.disabledClass);
20460                 e.dom.disabled = false;
20461             });
20462         }
20463         
20464         this.disabled = false;
20465         this.fireEvent("enable", this);
20466         return this;
20467     }
20468
20469 });
20470
20471 Roo.apply(Roo.bootstrap.CheckBox, {
20472     
20473     groups: {},
20474     
20475      /**
20476     * register a CheckBox Group
20477     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20478     */
20479     register : function(checkbox)
20480     {
20481         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20482             this.groups[checkbox.groupId] = {};
20483         }
20484         
20485         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20486             return;
20487         }
20488         
20489         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20490         
20491     },
20492     /**
20493     * fetch a CheckBox Group based on the group ID
20494     * @param {string} the group ID
20495     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20496     */
20497     get: function(groupId) {
20498         if (typeof(this.groups[groupId]) == 'undefined') {
20499             return false;
20500         }
20501         
20502         return this.groups[groupId] ;
20503     }
20504     
20505     
20506 });
20507 /*
20508  * - LGPL
20509  *
20510  * RadioItem
20511  * 
20512  */
20513
20514 /**
20515  * @class Roo.bootstrap.Radio
20516  * @extends Roo.bootstrap.Component
20517  * Bootstrap Radio class
20518  * @cfg {String} boxLabel - the label associated
20519  * @cfg {String} value - the value of radio
20520  * 
20521  * @constructor
20522  * Create a new Radio
20523  * @param {Object} config The config object
20524  */
20525 Roo.bootstrap.Radio = function(config){
20526     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20527     
20528 };
20529
20530 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20531     
20532     boxLabel : '',
20533     
20534     value : '',
20535     
20536     getAutoCreate : function()
20537     {
20538         var cfg = {
20539             tag : 'div',
20540             cls : 'form-group radio',
20541             cn : [
20542                 {
20543                     tag : 'label',
20544                     cls : 'box-label',
20545                     html : this.boxLabel
20546                 }
20547             ]
20548         };
20549         
20550         return cfg;
20551     },
20552     
20553     initEvents : function() 
20554     {
20555         this.parent().register(this);
20556         
20557         this.el.on('click', this.onClick, this);
20558         
20559     },
20560     
20561     onClick : function()
20562     {
20563         this.setChecked(true);
20564     },
20565     
20566     setChecked : function(state, suppressEvent)
20567     {
20568         this.parent().setValue(this.value, suppressEvent);
20569         
20570     }
20571     
20572 });
20573  
20574
20575  //<script type="text/javascript">
20576
20577 /*
20578  * Based  Ext JS Library 1.1.1
20579  * Copyright(c) 2006-2007, Ext JS, LLC.
20580  * LGPL
20581  *
20582  */
20583  
20584 /**
20585  * @class Roo.HtmlEditorCore
20586  * @extends Roo.Component
20587  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20588  *
20589  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20590  */
20591
20592 Roo.HtmlEditorCore = function(config){
20593     
20594     
20595     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20596     
20597     
20598     this.addEvents({
20599         /**
20600          * @event initialize
20601          * Fires when the editor is fully initialized (including the iframe)
20602          * @param {Roo.HtmlEditorCore} this
20603          */
20604         initialize: true,
20605         /**
20606          * @event activate
20607          * Fires when the editor is first receives the focus. Any insertion must wait
20608          * until after this event.
20609          * @param {Roo.HtmlEditorCore} this
20610          */
20611         activate: true,
20612          /**
20613          * @event beforesync
20614          * Fires before the textarea is updated with content from the editor iframe. Return false
20615          * to cancel the sync.
20616          * @param {Roo.HtmlEditorCore} this
20617          * @param {String} html
20618          */
20619         beforesync: true,
20620          /**
20621          * @event beforepush
20622          * Fires before the iframe editor is updated with content from the textarea. Return false
20623          * to cancel the push.
20624          * @param {Roo.HtmlEditorCore} this
20625          * @param {String} html
20626          */
20627         beforepush: true,
20628          /**
20629          * @event sync
20630          * Fires when the textarea is updated with content from the editor iframe.
20631          * @param {Roo.HtmlEditorCore} this
20632          * @param {String} html
20633          */
20634         sync: true,
20635          /**
20636          * @event push
20637          * Fires when the iframe editor is updated with content from the textarea.
20638          * @param {Roo.HtmlEditorCore} this
20639          * @param {String} html
20640          */
20641         push: true,
20642         
20643         /**
20644          * @event editorevent
20645          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20646          * @param {Roo.HtmlEditorCore} this
20647          */
20648         editorevent: true
20649         
20650     });
20651     
20652     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20653     
20654     // defaults : white / black...
20655     this.applyBlacklists();
20656     
20657     
20658     
20659 };
20660
20661
20662 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20663
20664
20665      /**
20666      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20667      */
20668     
20669     owner : false,
20670     
20671      /**
20672      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20673      *                        Roo.resizable.
20674      */
20675     resizable : false,
20676      /**
20677      * @cfg {Number} height (in pixels)
20678      */   
20679     height: 300,
20680    /**
20681      * @cfg {Number} width (in pixels)
20682      */   
20683     width: 500,
20684     
20685     /**
20686      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20687      * 
20688      */
20689     stylesheets: false,
20690     
20691     // id of frame..
20692     frameId: false,
20693     
20694     // private properties
20695     validationEvent : false,
20696     deferHeight: true,
20697     initialized : false,
20698     activated : false,
20699     sourceEditMode : false,
20700     onFocus : Roo.emptyFn,
20701     iframePad:3,
20702     hideMode:'offsets',
20703     
20704     clearUp: true,
20705     
20706     // blacklist + whitelisted elements..
20707     black: false,
20708     white: false,
20709      
20710     
20711
20712     /**
20713      * Protected method that will not generally be called directly. It
20714      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20715      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20716      */
20717     getDocMarkup : function(){
20718         // body styles..
20719         var st = '';
20720         
20721         // inherit styels from page...?? 
20722         if (this.stylesheets === false) {
20723             
20724             Roo.get(document.head).select('style').each(function(node) {
20725                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20726             });
20727             
20728             Roo.get(document.head).select('link').each(function(node) { 
20729                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20730             });
20731             
20732         } else if (!this.stylesheets.length) {
20733                 // simple..
20734                 st = '<style type="text/css">' +
20735                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20736                    '</style>';
20737         } else { 
20738             
20739         }
20740         
20741         st +=  '<style type="text/css">' +
20742             'IMG { cursor: pointer } ' +
20743         '</style>';
20744
20745         
20746         return '<html><head>' + st  +
20747             //<style type="text/css">' +
20748             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20749             //'</style>' +
20750             ' </head><body class="roo-htmleditor-body"></body></html>';
20751     },
20752
20753     // private
20754     onRender : function(ct, position)
20755     {
20756         var _t = this;
20757         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20758         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20759         
20760         
20761         this.el.dom.style.border = '0 none';
20762         this.el.dom.setAttribute('tabIndex', -1);
20763         this.el.addClass('x-hidden hide');
20764         
20765         
20766         
20767         if(Roo.isIE){ // fix IE 1px bogus margin
20768             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20769         }
20770        
20771         
20772         this.frameId = Roo.id();
20773         
20774          
20775         
20776         var iframe = this.owner.wrap.createChild({
20777             tag: 'iframe',
20778             cls: 'form-control', // bootstrap..
20779             id: this.frameId,
20780             name: this.frameId,
20781             frameBorder : 'no',
20782             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20783         }, this.el
20784         );
20785         
20786         
20787         this.iframe = iframe.dom;
20788
20789          this.assignDocWin();
20790         
20791         this.doc.designMode = 'on';
20792        
20793         this.doc.open();
20794         this.doc.write(this.getDocMarkup());
20795         this.doc.close();
20796
20797         
20798         var task = { // must defer to wait for browser to be ready
20799             run : function(){
20800                 //console.log("run task?" + this.doc.readyState);
20801                 this.assignDocWin();
20802                 if(this.doc.body || this.doc.readyState == 'complete'){
20803                     try {
20804                         this.doc.designMode="on";
20805                     } catch (e) {
20806                         return;
20807                     }
20808                     Roo.TaskMgr.stop(task);
20809                     this.initEditor.defer(10, this);
20810                 }
20811             },
20812             interval : 10,
20813             duration: 10000,
20814             scope: this
20815         };
20816         Roo.TaskMgr.start(task);
20817
20818     },
20819
20820     // private
20821     onResize : function(w, h)
20822     {
20823          Roo.log('resize: ' +w + ',' + h );
20824         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20825         if(!this.iframe){
20826             return;
20827         }
20828         if(typeof w == 'number'){
20829             
20830             this.iframe.style.width = w + 'px';
20831         }
20832         if(typeof h == 'number'){
20833             
20834             this.iframe.style.height = h + 'px';
20835             if(this.doc){
20836                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20837             }
20838         }
20839         
20840     },
20841
20842     /**
20843      * Toggles the editor between standard and source edit mode.
20844      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20845      */
20846     toggleSourceEdit : function(sourceEditMode){
20847         
20848         this.sourceEditMode = sourceEditMode === true;
20849         
20850         if(this.sourceEditMode){
20851  
20852             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20853             
20854         }else{
20855             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20856             //this.iframe.className = '';
20857             this.deferFocus();
20858         }
20859         //this.setSize(this.owner.wrap.getSize());
20860         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20861     },
20862
20863     
20864   
20865
20866     /**
20867      * Protected method that will not generally be called directly. If you need/want
20868      * custom HTML cleanup, this is the method you should override.
20869      * @param {String} html The HTML to be cleaned
20870      * return {String} The cleaned HTML
20871      */
20872     cleanHtml : function(html){
20873         html = String(html);
20874         if(html.length > 5){
20875             if(Roo.isSafari){ // strip safari nonsense
20876                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20877             }
20878         }
20879         if(html == '&nbsp;'){
20880             html = '';
20881         }
20882         return html;
20883     },
20884
20885     /**
20886      * HTML Editor -> Textarea
20887      * Protected method that will not generally be called directly. Syncs the contents
20888      * of the editor iframe with the textarea.
20889      */
20890     syncValue : function(){
20891         if(this.initialized){
20892             var bd = (this.doc.body || this.doc.documentElement);
20893             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20894             var html = bd.innerHTML;
20895             if(Roo.isSafari){
20896                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20897                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20898                 if(m && m[1]){
20899                     html = '<div style="'+m[0]+'">' + html + '</div>';
20900                 }
20901             }
20902             html = this.cleanHtml(html);
20903             // fix up the special chars.. normaly like back quotes in word...
20904             // however we do not want to do this with chinese..
20905             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20906                 var cc = b.charCodeAt();
20907                 if (
20908                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20909                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20910                     (cc >= 0xf900 && cc < 0xfb00 )
20911                 ) {
20912                         return b;
20913                 }
20914                 return "&#"+cc+";" 
20915             });
20916             if(this.owner.fireEvent('beforesync', this, html) !== false){
20917                 this.el.dom.value = html;
20918                 this.owner.fireEvent('sync', this, html);
20919             }
20920         }
20921     },
20922
20923     /**
20924      * Protected method that will not generally be called directly. Pushes the value of the textarea
20925      * into the iframe editor.
20926      */
20927     pushValue : function(){
20928         if(this.initialized){
20929             var v = this.el.dom.value.trim();
20930             
20931 //            if(v.length < 1){
20932 //                v = '&#160;';
20933 //            }
20934             
20935             if(this.owner.fireEvent('beforepush', this, v) !== false){
20936                 var d = (this.doc.body || this.doc.documentElement);
20937                 d.innerHTML = v;
20938                 this.cleanUpPaste();
20939                 this.el.dom.value = d.innerHTML;
20940                 this.owner.fireEvent('push', this, v);
20941             }
20942         }
20943     },
20944
20945     // private
20946     deferFocus : function(){
20947         this.focus.defer(10, this);
20948     },
20949
20950     // doc'ed in Field
20951     focus : function(){
20952         if(this.win && !this.sourceEditMode){
20953             this.win.focus();
20954         }else{
20955             this.el.focus();
20956         }
20957     },
20958     
20959     assignDocWin: function()
20960     {
20961         var iframe = this.iframe;
20962         
20963          if(Roo.isIE){
20964             this.doc = iframe.contentWindow.document;
20965             this.win = iframe.contentWindow;
20966         } else {
20967 //            if (!Roo.get(this.frameId)) {
20968 //                return;
20969 //            }
20970 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20971 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20972             
20973             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20974                 return;
20975             }
20976             
20977             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20978             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20979         }
20980     },
20981     
20982     // private
20983     initEditor : function(){
20984         //console.log("INIT EDITOR");
20985         this.assignDocWin();
20986         
20987         
20988         
20989         this.doc.designMode="on";
20990         this.doc.open();
20991         this.doc.write(this.getDocMarkup());
20992         this.doc.close();
20993         
20994         var dbody = (this.doc.body || this.doc.documentElement);
20995         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20996         // this copies styles from the containing element into thsi one..
20997         // not sure why we need all of this..
20998         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20999         
21000         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21001         //ss['background-attachment'] = 'fixed'; // w3c
21002         dbody.bgProperties = 'fixed'; // ie
21003         //Roo.DomHelper.applyStyles(dbody, ss);
21004         Roo.EventManager.on(this.doc, {
21005             //'mousedown': this.onEditorEvent,
21006             'mouseup': this.onEditorEvent,
21007             'dblclick': this.onEditorEvent,
21008             'click': this.onEditorEvent,
21009             'keyup': this.onEditorEvent,
21010             buffer:100,
21011             scope: this
21012         });
21013         if(Roo.isGecko){
21014             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21015         }
21016         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21017             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21018         }
21019         this.initialized = true;
21020
21021         this.owner.fireEvent('initialize', this);
21022         this.pushValue();
21023     },
21024
21025     // private
21026     onDestroy : function(){
21027         
21028         
21029         
21030         if(this.rendered){
21031             
21032             //for (var i =0; i < this.toolbars.length;i++) {
21033             //    // fixme - ask toolbars for heights?
21034             //    this.toolbars[i].onDestroy();
21035            // }
21036             
21037             //this.wrap.dom.innerHTML = '';
21038             //this.wrap.remove();
21039         }
21040     },
21041
21042     // private
21043     onFirstFocus : function(){
21044         
21045         this.assignDocWin();
21046         
21047         
21048         this.activated = true;
21049          
21050     
21051         if(Roo.isGecko){ // prevent silly gecko errors
21052             this.win.focus();
21053             var s = this.win.getSelection();
21054             if(!s.focusNode || s.focusNode.nodeType != 3){
21055                 var r = s.getRangeAt(0);
21056                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21057                 r.collapse(true);
21058                 this.deferFocus();
21059             }
21060             try{
21061                 this.execCmd('useCSS', true);
21062                 this.execCmd('styleWithCSS', false);
21063             }catch(e){}
21064         }
21065         this.owner.fireEvent('activate', this);
21066     },
21067
21068     // private
21069     adjustFont: function(btn){
21070         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21071         //if(Roo.isSafari){ // safari
21072         //    adjust *= 2;
21073        // }
21074         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21075         if(Roo.isSafari){ // safari
21076             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21077             v =  (v < 10) ? 10 : v;
21078             v =  (v > 48) ? 48 : v;
21079             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21080             
21081         }
21082         
21083         
21084         v = Math.max(1, v+adjust);
21085         
21086         this.execCmd('FontSize', v  );
21087     },
21088
21089     onEditorEvent : function(e)
21090     {
21091         this.owner.fireEvent('editorevent', this, e);
21092       //  this.updateToolbar();
21093         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21094     },
21095
21096     insertTag : function(tg)
21097     {
21098         // could be a bit smarter... -> wrap the current selected tRoo..
21099         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21100             
21101             range = this.createRange(this.getSelection());
21102             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21103             wrappingNode.appendChild(range.extractContents());
21104             range.insertNode(wrappingNode);
21105
21106             return;
21107             
21108             
21109             
21110         }
21111         this.execCmd("formatblock",   tg);
21112         
21113     },
21114     
21115     insertText : function(txt)
21116     {
21117         
21118         
21119         var range = this.createRange();
21120         range.deleteContents();
21121                //alert(Sender.getAttribute('label'));
21122                
21123         range.insertNode(this.doc.createTextNode(txt));
21124     } ,
21125     
21126      
21127
21128     /**
21129      * Executes a Midas editor command on the editor document and performs necessary focus and
21130      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21131      * @param {String} cmd The Midas command
21132      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21133      */
21134     relayCmd : function(cmd, value){
21135         this.win.focus();
21136         this.execCmd(cmd, value);
21137         this.owner.fireEvent('editorevent', this);
21138         //this.updateToolbar();
21139         this.owner.deferFocus();
21140     },
21141
21142     /**
21143      * Executes a Midas editor command directly on the editor document.
21144      * For visual commands, you should use {@link #relayCmd} instead.
21145      * <b>This should only be called after the editor is initialized.</b>
21146      * @param {String} cmd The Midas command
21147      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21148      */
21149     execCmd : function(cmd, value){
21150         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21151         this.syncValue();
21152     },
21153  
21154  
21155    
21156     /**
21157      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21158      * to insert tRoo.
21159      * @param {String} text | dom node.. 
21160      */
21161     insertAtCursor : function(text)
21162     {
21163         
21164         
21165         
21166         if(!this.activated){
21167             return;
21168         }
21169         /*
21170         if(Roo.isIE){
21171             this.win.focus();
21172             var r = this.doc.selection.createRange();
21173             if(r){
21174                 r.collapse(true);
21175                 r.pasteHTML(text);
21176                 this.syncValue();
21177                 this.deferFocus();
21178             
21179             }
21180             return;
21181         }
21182         */
21183         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21184             this.win.focus();
21185             
21186             
21187             // from jquery ui (MIT licenced)
21188             var range, node;
21189             var win = this.win;
21190             
21191             if (win.getSelection && win.getSelection().getRangeAt) {
21192                 range = win.getSelection().getRangeAt(0);
21193                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21194                 range.insertNode(node);
21195             } else if (win.document.selection && win.document.selection.createRange) {
21196                 // no firefox support
21197                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21198                 win.document.selection.createRange().pasteHTML(txt);
21199             } else {
21200                 // no firefox support
21201                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21202                 this.execCmd('InsertHTML', txt);
21203             } 
21204             
21205             this.syncValue();
21206             
21207             this.deferFocus();
21208         }
21209     },
21210  // private
21211     mozKeyPress : function(e){
21212         if(e.ctrlKey){
21213             var c = e.getCharCode(), cmd;
21214           
21215             if(c > 0){
21216                 c = String.fromCharCode(c).toLowerCase();
21217                 switch(c){
21218                     case 'b':
21219                         cmd = 'bold';
21220                         break;
21221                     case 'i':
21222                         cmd = 'italic';
21223                         break;
21224                     
21225                     case 'u':
21226                         cmd = 'underline';
21227                         break;
21228                     
21229                     case 'v':
21230                         this.cleanUpPaste.defer(100, this);
21231                         return;
21232                         
21233                 }
21234                 if(cmd){
21235                     this.win.focus();
21236                     this.execCmd(cmd);
21237                     this.deferFocus();
21238                     e.preventDefault();
21239                 }
21240                 
21241             }
21242         }
21243     },
21244
21245     // private
21246     fixKeys : function(){ // load time branching for fastest keydown performance
21247         if(Roo.isIE){
21248             return function(e){
21249                 var k = e.getKey(), r;
21250                 if(k == e.TAB){
21251                     e.stopEvent();
21252                     r = this.doc.selection.createRange();
21253                     if(r){
21254                         r.collapse(true);
21255                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21256                         this.deferFocus();
21257                     }
21258                     return;
21259                 }
21260                 
21261                 if(k == e.ENTER){
21262                     r = this.doc.selection.createRange();
21263                     if(r){
21264                         var target = r.parentElement();
21265                         if(!target || target.tagName.toLowerCase() != 'li'){
21266                             e.stopEvent();
21267                             r.pasteHTML('<br />');
21268                             r.collapse(false);
21269                             r.select();
21270                         }
21271                     }
21272                 }
21273                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21274                     this.cleanUpPaste.defer(100, this);
21275                     return;
21276                 }
21277                 
21278                 
21279             };
21280         }else if(Roo.isOpera){
21281             return function(e){
21282                 var k = e.getKey();
21283                 if(k == e.TAB){
21284                     e.stopEvent();
21285                     this.win.focus();
21286                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21287                     this.deferFocus();
21288                 }
21289                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21290                     this.cleanUpPaste.defer(100, this);
21291                     return;
21292                 }
21293                 
21294             };
21295         }else if(Roo.isSafari){
21296             return function(e){
21297                 var k = e.getKey();
21298                 
21299                 if(k == e.TAB){
21300                     e.stopEvent();
21301                     this.execCmd('InsertText','\t');
21302                     this.deferFocus();
21303                     return;
21304                 }
21305                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21306                     this.cleanUpPaste.defer(100, this);
21307                     return;
21308                 }
21309                 
21310              };
21311         }
21312     }(),
21313     
21314     getAllAncestors: function()
21315     {
21316         var p = this.getSelectedNode();
21317         var a = [];
21318         if (!p) {
21319             a.push(p); // push blank onto stack..
21320             p = this.getParentElement();
21321         }
21322         
21323         
21324         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21325             a.push(p);
21326             p = p.parentNode;
21327         }
21328         a.push(this.doc.body);
21329         return a;
21330     },
21331     lastSel : false,
21332     lastSelNode : false,
21333     
21334     
21335     getSelection : function() 
21336     {
21337         this.assignDocWin();
21338         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21339     },
21340     
21341     getSelectedNode: function() 
21342     {
21343         // this may only work on Gecko!!!
21344         
21345         // should we cache this!!!!
21346         
21347         
21348         
21349          
21350         var range = this.createRange(this.getSelection()).cloneRange();
21351         
21352         if (Roo.isIE) {
21353             var parent = range.parentElement();
21354             while (true) {
21355                 var testRange = range.duplicate();
21356                 testRange.moveToElementText(parent);
21357                 if (testRange.inRange(range)) {
21358                     break;
21359                 }
21360                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21361                     break;
21362                 }
21363                 parent = parent.parentElement;
21364             }
21365             return parent;
21366         }
21367         
21368         // is ancestor a text element.
21369         var ac =  range.commonAncestorContainer;
21370         if (ac.nodeType == 3) {
21371             ac = ac.parentNode;
21372         }
21373         
21374         var ar = ac.childNodes;
21375          
21376         var nodes = [];
21377         var other_nodes = [];
21378         var has_other_nodes = false;
21379         for (var i=0;i<ar.length;i++) {
21380             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21381                 continue;
21382             }
21383             // fullly contained node.
21384             
21385             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21386                 nodes.push(ar[i]);
21387                 continue;
21388             }
21389             
21390             // probably selected..
21391             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21392                 other_nodes.push(ar[i]);
21393                 continue;
21394             }
21395             // outer..
21396             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21397                 continue;
21398             }
21399             
21400             
21401             has_other_nodes = true;
21402         }
21403         if (!nodes.length && other_nodes.length) {
21404             nodes= other_nodes;
21405         }
21406         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21407             return false;
21408         }
21409         
21410         return nodes[0];
21411     },
21412     createRange: function(sel)
21413     {
21414         // this has strange effects when using with 
21415         // top toolbar - not sure if it's a great idea.
21416         //this.editor.contentWindow.focus();
21417         if (typeof sel != "undefined") {
21418             try {
21419                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21420             } catch(e) {
21421                 return this.doc.createRange();
21422             }
21423         } else {
21424             return this.doc.createRange();
21425         }
21426     },
21427     getParentElement: function()
21428     {
21429         
21430         this.assignDocWin();
21431         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21432         
21433         var range = this.createRange(sel);
21434          
21435         try {
21436             var p = range.commonAncestorContainer;
21437             while (p.nodeType == 3) { // text node
21438                 p = p.parentNode;
21439             }
21440             return p;
21441         } catch (e) {
21442             return null;
21443         }
21444     
21445     },
21446     /***
21447      *
21448      * Range intersection.. the hard stuff...
21449      *  '-1' = before
21450      *  '0' = hits..
21451      *  '1' = after.
21452      *         [ -- selected range --- ]
21453      *   [fail]                        [fail]
21454      *
21455      *    basically..
21456      *      if end is before start or  hits it. fail.
21457      *      if start is after end or hits it fail.
21458      *
21459      *   if either hits (but other is outside. - then it's not 
21460      *   
21461      *    
21462      **/
21463     
21464     
21465     // @see http://www.thismuchiknow.co.uk/?p=64.
21466     rangeIntersectsNode : function(range, node)
21467     {
21468         var nodeRange = node.ownerDocument.createRange();
21469         try {
21470             nodeRange.selectNode(node);
21471         } catch (e) {
21472             nodeRange.selectNodeContents(node);
21473         }
21474     
21475         var rangeStartRange = range.cloneRange();
21476         rangeStartRange.collapse(true);
21477     
21478         var rangeEndRange = range.cloneRange();
21479         rangeEndRange.collapse(false);
21480     
21481         var nodeStartRange = nodeRange.cloneRange();
21482         nodeStartRange.collapse(true);
21483     
21484         var nodeEndRange = nodeRange.cloneRange();
21485         nodeEndRange.collapse(false);
21486     
21487         return rangeStartRange.compareBoundaryPoints(
21488                  Range.START_TO_START, nodeEndRange) == -1 &&
21489                rangeEndRange.compareBoundaryPoints(
21490                  Range.START_TO_START, nodeStartRange) == 1;
21491         
21492          
21493     },
21494     rangeCompareNode : function(range, node)
21495     {
21496         var nodeRange = node.ownerDocument.createRange();
21497         try {
21498             nodeRange.selectNode(node);
21499         } catch (e) {
21500             nodeRange.selectNodeContents(node);
21501         }
21502         
21503         
21504         range.collapse(true);
21505     
21506         nodeRange.collapse(true);
21507      
21508         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21509         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21510          
21511         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21512         
21513         var nodeIsBefore   =  ss == 1;
21514         var nodeIsAfter    = ee == -1;
21515         
21516         if (nodeIsBefore && nodeIsAfter) {
21517             return 0; // outer
21518         }
21519         if (!nodeIsBefore && nodeIsAfter) {
21520             return 1; //right trailed.
21521         }
21522         
21523         if (nodeIsBefore && !nodeIsAfter) {
21524             return 2;  // left trailed.
21525         }
21526         // fully contined.
21527         return 3;
21528     },
21529
21530     // private? - in a new class?
21531     cleanUpPaste :  function()
21532     {
21533         // cleans up the whole document..
21534         Roo.log('cleanuppaste');
21535         
21536         this.cleanUpChildren(this.doc.body);
21537         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21538         if (clean != this.doc.body.innerHTML) {
21539             this.doc.body.innerHTML = clean;
21540         }
21541         
21542     },
21543     
21544     cleanWordChars : function(input) {// change the chars to hex code
21545         var he = Roo.HtmlEditorCore;
21546         
21547         var output = input;
21548         Roo.each(he.swapCodes, function(sw) { 
21549             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21550             
21551             output = output.replace(swapper, sw[1]);
21552         });
21553         
21554         return output;
21555     },
21556     
21557     
21558     cleanUpChildren : function (n)
21559     {
21560         if (!n.childNodes.length) {
21561             return;
21562         }
21563         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21564            this.cleanUpChild(n.childNodes[i]);
21565         }
21566     },
21567     
21568     
21569         
21570     
21571     cleanUpChild : function (node)
21572     {
21573         var ed = this;
21574         //console.log(node);
21575         if (node.nodeName == "#text") {
21576             // clean up silly Windows -- stuff?
21577             return; 
21578         }
21579         if (node.nodeName == "#comment") {
21580             node.parentNode.removeChild(node);
21581             // clean up silly Windows -- stuff?
21582             return; 
21583         }
21584         var lcname = node.tagName.toLowerCase();
21585         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21586         // whitelist of tags..
21587         
21588         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21589             // remove node.
21590             node.parentNode.removeChild(node);
21591             return;
21592             
21593         }
21594         
21595         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21596         
21597         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21598         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21599         
21600         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21601         //    remove_keep_children = true;
21602         //}
21603         
21604         if (remove_keep_children) {
21605             this.cleanUpChildren(node);
21606             // inserts everything just before this node...
21607             while (node.childNodes.length) {
21608                 var cn = node.childNodes[0];
21609                 node.removeChild(cn);
21610                 node.parentNode.insertBefore(cn, node);
21611             }
21612             node.parentNode.removeChild(node);
21613             return;
21614         }
21615         
21616         if (!node.attributes || !node.attributes.length) {
21617             this.cleanUpChildren(node);
21618             return;
21619         }
21620         
21621         function cleanAttr(n,v)
21622         {
21623             
21624             if (v.match(/^\./) || v.match(/^\//)) {
21625                 return;
21626             }
21627             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21628                 return;
21629             }
21630             if (v.match(/^#/)) {
21631                 return;
21632             }
21633 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21634             node.removeAttribute(n);
21635             
21636         }
21637         
21638         var cwhite = this.cwhite;
21639         var cblack = this.cblack;
21640             
21641         function cleanStyle(n,v)
21642         {
21643             if (v.match(/expression/)) { //XSS?? should we even bother..
21644                 node.removeAttribute(n);
21645                 return;
21646             }
21647             
21648             var parts = v.split(/;/);
21649             var clean = [];
21650             
21651             Roo.each(parts, function(p) {
21652                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21653                 if (!p.length) {
21654                     return true;
21655                 }
21656                 var l = p.split(':').shift().replace(/\s+/g,'');
21657                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21658                 
21659                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21660 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21661                     //node.removeAttribute(n);
21662                     return true;
21663                 }
21664                 //Roo.log()
21665                 // only allow 'c whitelisted system attributes'
21666                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21667 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21668                     //node.removeAttribute(n);
21669                     return true;
21670                 }
21671                 
21672                 
21673                  
21674                 
21675                 clean.push(p);
21676                 return true;
21677             });
21678             if (clean.length) { 
21679                 node.setAttribute(n, clean.join(';'));
21680             } else {
21681                 node.removeAttribute(n);
21682             }
21683             
21684         }
21685         
21686         
21687         for (var i = node.attributes.length-1; i > -1 ; i--) {
21688             var a = node.attributes[i];
21689             //console.log(a);
21690             
21691             if (a.name.toLowerCase().substr(0,2)=='on')  {
21692                 node.removeAttribute(a.name);
21693                 continue;
21694             }
21695             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21696                 node.removeAttribute(a.name);
21697                 continue;
21698             }
21699             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21700                 cleanAttr(a.name,a.value); // fixme..
21701                 continue;
21702             }
21703             if (a.name == 'style') {
21704                 cleanStyle(a.name,a.value);
21705                 continue;
21706             }
21707             /// clean up MS crap..
21708             // tecnically this should be a list of valid class'es..
21709             
21710             
21711             if (a.name == 'class') {
21712                 if (a.value.match(/^Mso/)) {
21713                     node.className = '';
21714                 }
21715                 
21716                 if (a.value.match(/body/)) {
21717                     node.className = '';
21718                 }
21719                 continue;
21720             }
21721             
21722             // style cleanup!?
21723             // class cleanup?
21724             
21725         }
21726         
21727         
21728         this.cleanUpChildren(node);
21729         
21730         
21731     },
21732     
21733     /**
21734      * Clean up MS wordisms...
21735      */
21736     cleanWord : function(node)
21737     {
21738         
21739         
21740         if (!node) {
21741             this.cleanWord(this.doc.body);
21742             return;
21743         }
21744         if (node.nodeName == "#text") {
21745             // clean up silly Windows -- stuff?
21746             return; 
21747         }
21748         if (node.nodeName == "#comment") {
21749             node.parentNode.removeChild(node);
21750             // clean up silly Windows -- stuff?
21751             return; 
21752         }
21753         
21754         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21755             node.parentNode.removeChild(node);
21756             return;
21757         }
21758         
21759         // remove - but keep children..
21760         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21761             while (node.childNodes.length) {
21762                 var cn = node.childNodes[0];
21763                 node.removeChild(cn);
21764                 node.parentNode.insertBefore(cn, node);
21765             }
21766             node.parentNode.removeChild(node);
21767             this.iterateChildren(node, this.cleanWord);
21768             return;
21769         }
21770         // clean styles
21771         if (node.className.length) {
21772             
21773             var cn = node.className.split(/\W+/);
21774             var cna = [];
21775             Roo.each(cn, function(cls) {
21776                 if (cls.match(/Mso[a-zA-Z]+/)) {
21777                     return;
21778                 }
21779                 cna.push(cls);
21780             });
21781             node.className = cna.length ? cna.join(' ') : '';
21782             if (!cna.length) {
21783                 node.removeAttribute("class");
21784             }
21785         }
21786         
21787         if (node.hasAttribute("lang")) {
21788             node.removeAttribute("lang");
21789         }
21790         
21791         if (node.hasAttribute("style")) {
21792             
21793             var styles = node.getAttribute("style").split(";");
21794             var nstyle = [];
21795             Roo.each(styles, function(s) {
21796                 if (!s.match(/:/)) {
21797                     return;
21798                 }
21799                 var kv = s.split(":");
21800                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21801                     return;
21802                 }
21803                 // what ever is left... we allow.
21804                 nstyle.push(s);
21805             });
21806             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21807             if (!nstyle.length) {
21808                 node.removeAttribute('style');
21809             }
21810         }
21811         this.iterateChildren(node, this.cleanWord);
21812         
21813         
21814         
21815     },
21816     /**
21817      * iterateChildren of a Node, calling fn each time, using this as the scole..
21818      * @param {DomNode} node node to iterate children of.
21819      * @param {Function} fn method of this class to call on each item.
21820      */
21821     iterateChildren : function(node, fn)
21822     {
21823         if (!node.childNodes.length) {
21824                 return;
21825         }
21826         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21827            fn.call(this, node.childNodes[i])
21828         }
21829     },
21830     
21831     
21832     /**
21833      * cleanTableWidths.
21834      *
21835      * Quite often pasting from word etc.. results in tables with column and widths.
21836      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21837      *
21838      */
21839     cleanTableWidths : function(node)
21840     {
21841          
21842          
21843         if (!node) {
21844             this.cleanTableWidths(this.doc.body);
21845             return;
21846         }
21847         
21848         // ignore list...
21849         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21850             return; 
21851         }
21852         Roo.log(node.tagName);
21853         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21854             this.iterateChildren(node, this.cleanTableWidths);
21855             return;
21856         }
21857         if (node.hasAttribute('width')) {
21858             node.removeAttribute('width');
21859         }
21860         
21861          
21862         if (node.hasAttribute("style")) {
21863             // pretty basic...
21864             
21865             var styles = node.getAttribute("style").split(";");
21866             var nstyle = [];
21867             Roo.each(styles, function(s) {
21868                 if (!s.match(/:/)) {
21869                     return;
21870                 }
21871                 var kv = s.split(":");
21872                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21873                     return;
21874                 }
21875                 // what ever is left... we allow.
21876                 nstyle.push(s);
21877             });
21878             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21879             if (!nstyle.length) {
21880                 node.removeAttribute('style');
21881             }
21882         }
21883         
21884         this.iterateChildren(node, this.cleanTableWidths);
21885         
21886         
21887     },
21888     
21889     
21890     
21891     
21892     domToHTML : function(currentElement, depth, nopadtext) {
21893         
21894         depth = depth || 0;
21895         nopadtext = nopadtext || false;
21896     
21897         if (!currentElement) {
21898             return this.domToHTML(this.doc.body);
21899         }
21900         
21901         //Roo.log(currentElement);
21902         var j;
21903         var allText = false;
21904         var nodeName = currentElement.nodeName;
21905         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21906         
21907         if  (nodeName == '#text') {
21908             
21909             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21910         }
21911         
21912         
21913         var ret = '';
21914         if (nodeName != 'BODY') {
21915              
21916             var i = 0;
21917             // Prints the node tagName, such as <A>, <IMG>, etc
21918             if (tagName) {
21919                 var attr = [];
21920                 for(i = 0; i < currentElement.attributes.length;i++) {
21921                     // quoting?
21922                     var aname = currentElement.attributes.item(i).name;
21923                     if (!currentElement.attributes.item(i).value.length) {
21924                         continue;
21925                     }
21926                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21927                 }
21928                 
21929                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21930             } 
21931             else {
21932                 
21933                 // eack
21934             }
21935         } else {
21936             tagName = false;
21937         }
21938         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21939             return ret;
21940         }
21941         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21942             nopadtext = true;
21943         }
21944         
21945         
21946         // Traverse the tree
21947         i = 0;
21948         var currentElementChild = currentElement.childNodes.item(i);
21949         var allText = true;
21950         var innerHTML  = '';
21951         lastnode = '';
21952         while (currentElementChild) {
21953             // Formatting code (indent the tree so it looks nice on the screen)
21954             var nopad = nopadtext;
21955             if (lastnode == 'SPAN') {
21956                 nopad  = true;
21957             }
21958             // text
21959             if  (currentElementChild.nodeName == '#text') {
21960                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21961                 toadd = nopadtext ? toadd : toadd.trim();
21962                 if (!nopad && toadd.length > 80) {
21963                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21964                 }
21965                 innerHTML  += toadd;
21966                 
21967                 i++;
21968                 currentElementChild = currentElement.childNodes.item(i);
21969                 lastNode = '';
21970                 continue;
21971             }
21972             allText = false;
21973             
21974             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21975                 
21976             // Recursively traverse the tree structure of the child node
21977             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21978             lastnode = currentElementChild.nodeName;
21979             i++;
21980             currentElementChild=currentElement.childNodes.item(i);
21981         }
21982         
21983         ret += innerHTML;
21984         
21985         if (!allText) {
21986                 // The remaining code is mostly for formatting the tree
21987             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21988         }
21989         
21990         
21991         if (tagName) {
21992             ret+= "</"+tagName+">";
21993         }
21994         return ret;
21995         
21996     },
21997         
21998     applyBlacklists : function()
21999     {
22000         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22001         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22002         
22003         this.white = [];
22004         this.black = [];
22005         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22006             if (b.indexOf(tag) > -1) {
22007                 return;
22008             }
22009             this.white.push(tag);
22010             
22011         }, this);
22012         
22013         Roo.each(w, function(tag) {
22014             if (b.indexOf(tag) > -1) {
22015                 return;
22016             }
22017             if (this.white.indexOf(tag) > -1) {
22018                 return;
22019             }
22020             this.white.push(tag);
22021             
22022         }, this);
22023         
22024         
22025         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22026             if (w.indexOf(tag) > -1) {
22027                 return;
22028             }
22029             this.black.push(tag);
22030             
22031         }, this);
22032         
22033         Roo.each(b, function(tag) {
22034             if (w.indexOf(tag) > -1) {
22035                 return;
22036             }
22037             if (this.black.indexOf(tag) > -1) {
22038                 return;
22039             }
22040             this.black.push(tag);
22041             
22042         }, this);
22043         
22044         
22045         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22046         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22047         
22048         this.cwhite = [];
22049         this.cblack = [];
22050         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22051             if (b.indexOf(tag) > -1) {
22052                 return;
22053             }
22054             this.cwhite.push(tag);
22055             
22056         }, this);
22057         
22058         Roo.each(w, function(tag) {
22059             if (b.indexOf(tag) > -1) {
22060                 return;
22061             }
22062             if (this.cwhite.indexOf(tag) > -1) {
22063                 return;
22064             }
22065             this.cwhite.push(tag);
22066             
22067         }, this);
22068         
22069         
22070         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22071             if (w.indexOf(tag) > -1) {
22072                 return;
22073             }
22074             this.cblack.push(tag);
22075             
22076         }, this);
22077         
22078         Roo.each(b, function(tag) {
22079             if (w.indexOf(tag) > -1) {
22080                 return;
22081             }
22082             if (this.cblack.indexOf(tag) > -1) {
22083                 return;
22084             }
22085             this.cblack.push(tag);
22086             
22087         }, this);
22088     },
22089     
22090     setStylesheets : function(stylesheets)
22091     {
22092         if(typeof(stylesheets) == 'string'){
22093             Roo.get(this.iframe.contentDocument.head).createChild({
22094                 tag : 'link',
22095                 rel : 'stylesheet',
22096                 type : 'text/css',
22097                 href : stylesheets
22098             });
22099             
22100             return;
22101         }
22102         var _this = this;
22103      
22104         Roo.each(stylesheets, function(s) {
22105             if(!s.length){
22106                 return;
22107             }
22108             
22109             Roo.get(_this.iframe.contentDocument.head).createChild({
22110                 tag : 'link',
22111                 rel : 'stylesheet',
22112                 type : 'text/css',
22113                 href : s
22114             });
22115         });
22116
22117         
22118     },
22119     
22120     removeStylesheets : function()
22121     {
22122         var _this = this;
22123         
22124         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22125             s.remove();
22126         });
22127     }
22128     
22129     // hide stuff that is not compatible
22130     /**
22131      * @event blur
22132      * @hide
22133      */
22134     /**
22135      * @event change
22136      * @hide
22137      */
22138     /**
22139      * @event focus
22140      * @hide
22141      */
22142     /**
22143      * @event specialkey
22144      * @hide
22145      */
22146     /**
22147      * @cfg {String} fieldClass @hide
22148      */
22149     /**
22150      * @cfg {String} focusClass @hide
22151      */
22152     /**
22153      * @cfg {String} autoCreate @hide
22154      */
22155     /**
22156      * @cfg {String} inputType @hide
22157      */
22158     /**
22159      * @cfg {String} invalidClass @hide
22160      */
22161     /**
22162      * @cfg {String} invalidText @hide
22163      */
22164     /**
22165      * @cfg {String} msgFx @hide
22166      */
22167     /**
22168      * @cfg {String} validateOnBlur @hide
22169      */
22170 });
22171
22172 Roo.HtmlEditorCore.white = [
22173         'area', 'br', 'img', 'input', 'hr', 'wbr',
22174         
22175        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22176        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22177        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22178        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22179        'table',   'ul',         'xmp', 
22180        
22181        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22182       'thead',   'tr', 
22183      
22184       'dir', 'menu', 'ol', 'ul', 'dl',
22185        
22186       'embed',  'object'
22187 ];
22188
22189
22190 Roo.HtmlEditorCore.black = [
22191     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22192         'applet', // 
22193         'base',   'basefont', 'bgsound', 'blink',  'body', 
22194         'frame',  'frameset', 'head',    'html',   'ilayer', 
22195         'iframe', 'layer',  'link',     'meta',    'object',   
22196         'script', 'style' ,'title',  'xml' // clean later..
22197 ];
22198 Roo.HtmlEditorCore.clean = [
22199     'script', 'style', 'title', 'xml'
22200 ];
22201 Roo.HtmlEditorCore.remove = [
22202     'font'
22203 ];
22204 // attributes..
22205
22206 Roo.HtmlEditorCore.ablack = [
22207     'on'
22208 ];
22209     
22210 Roo.HtmlEditorCore.aclean = [ 
22211     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22212 ];
22213
22214 // protocols..
22215 Roo.HtmlEditorCore.pwhite= [
22216         'http',  'https',  'mailto'
22217 ];
22218
22219 // white listed style attributes.
22220 Roo.HtmlEditorCore.cwhite= [
22221       //  'text-align', /// default is to allow most things..
22222       
22223          
22224 //        'font-size'//??
22225 ];
22226
22227 // black listed style attributes.
22228 Roo.HtmlEditorCore.cblack= [
22229       //  'font-size' -- this can be set by the project 
22230 ];
22231
22232
22233 Roo.HtmlEditorCore.swapCodes   =[ 
22234     [    8211, "--" ], 
22235     [    8212, "--" ], 
22236     [    8216,  "'" ],  
22237     [    8217, "'" ],  
22238     [    8220, '"' ],  
22239     [    8221, '"' ],  
22240     [    8226, "*" ],  
22241     [    8230, "..." ]
22242 ]; 
22243
22244     /*
22245  * - LGPL
22246  *
22247  * HtmlEditor
22248  * 
22249  */
22250
22251 /**
22252  * @class Roo.bootstrap.HtmlEditor
22253  * @extends Roo.bootstrap.TextArea
22254  * Bootstrap HtmlEditor class
22255
22256  * @constructor
22257  * Create a new HtmlEditor
22258  * @param {Object} config The config object
22259  */
22260
22261 Roo.bootstrap.HtmlEditor = function(config){
22262     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22263     if (!this.toolbars) {
22264         this.toolbars = [];
22265     }
22266     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22267     this.addEvents({
22268             /**
22269              * @event initialize
22270              * Fires when the editor is fully initialized (including the iframe)
22271              * @param {HtmlEditor} this
22272              */
22273             initialize: true,
22274             /**
22275              * @event activate
22276              * Fires when the editor is first receives the focus. Any insertion must wait
22277              * until after this event.
22278              * @param {HtmlEditor} this
22279              */
22280             activate: true,
22281              /**
22282              * @event beforesync
22283              * Fires before the textarea is updated with content from the editor iframe. Return false
22284              * to cancel the sync.
22285              * @param {HtmlEditor} this
22286              * @param {String} html
22287              */
22288             beforesync: true,
22289              /**
22290              * @event beforepush
22291              * Fires before the iframe editor is updated with content from the textarea. Return false
22292              * to cancel the push.
22293              * @param {HtmlEditor} this
22294              * @param {String} html
22295              */
22296             beforepush: true,
22297              /**
22298              * @event sync
22299              * Fires when the textarea is updated with content from the editor iframe.
22300              * @param {HtmlEditor} this
22301              * @param {String} html
22302              */
22303             sync: true,
22304              /**
22305              * @event push
22306              * Fires when the iframe editor is updated with content from the textarea.
22307              * @param {HtmlEditor} this
22308              * @param {String} html
22309              */
22310             push: true,
22311              /**
22312              * @event editmodechange
22313              * Fires when the editor switches edit modes
22314              * @param {HtmlEditor} this
22315              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22316              */
22317             editmodechange: true,
22318             /**
22319              * @event editorevent
22320              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22321              * @param {HtmlEditor} this
22322              */
22323             editorevent: true,
22324             /**
22325              * @event firstfocus
22326              * Fires when on first focus - needed by toolbars..
22327              * @param {HtmlEditor} this
22328              */
22329             firstfocus: true,
22330             /**
22331              * @event autosave
22332              * Auto save the htmlEditor value as a file into Events
22333              * @param {HtmlEditor} this
22334              */
22335             autosave: true,
22336             /**
22337              * @event savedpreview
22338              * preview the saved version of htmlEditor
22339              * @param {HtmlEditor} this
22340              */
22341             savedpreview: true
22342         });
22343 };
22344
22345
22346 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22347     
22348     
22349       /**
22350      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22351      */
22352     toolbars : false,
22353    
22354      /**
22355      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22356      *                        Roo.resizable.
22357      */
22358     resizable : false,
22359      /**
22360      * @cfg {Number} height (in pixels)
22361      */   
22362     height: 300,
22363    /**
22364      * @cfg {Number} width (in pixels)
22365      */   
22366     width: false,
22367     
22368     /**
22369      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22370      * 
22371      */
22372     stylesheets: false,
22373     
22374     // id of frame..
22375     frameId: false,
22376     
22377     // private properties
22378     validationEvent : false,
22379     deferHeight: true,
22380     initialized : false,
22381     activated : false,
22382     
22383     onFocus : Roo.emptyFn,
22384     iframePad:3,
22385     hideMode:'offsets',
22386     
22387     
22388     tbContainer : false,
22389     
22390     toolbarContainer :function() {
22391         return this.wrap.select('.x-html-editor-tb',true).first();
22392     },
22393
22394     /**
22395      * Protected method that will not generally be called directly. It
22396      * is called when the editor creates its toolbar. Override this method if you need to
22397      * add custom toolbar buttons.
22398      * @param {HtmlEditor} editor
22399      */
22400     createToolbar : function(){
22401         
22402         Roo.log("create toolbars");
22403         
22404         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22405         this.toolbars[0].render(this.toolbarContainer());
22406         
22407         return;
22408         
22409 //        if (!editor.toolbars || !editor.toolbars.length) {
22410 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22411 //        }
22412 //        
22413 //        for (var i =0 ; i < editor.toolbars.length;i++) {
22414 //            editor.toolbars[i] = Roo.factory(
22415 //                    typeof(editor.toolbars[i]) == 'string' ?
22416 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
22417 //                Roo.bootstrap.HtmlEditor);
22418 //            editor.toolbars[i].init(editor);
22419 //        }
22420     },
22421
22422      
22423     // private
22424     onRender : function(ct, position)
22425     {
22426        // Roo.log("Call onRender: " + this.xtype);
22427         var _t = this;
22428         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22429       
22430         this.wrap = this.inputEl().wrap({
22431             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22432         });
22433         
22434         this.editorcore.onRender(ct, position);
22435          
22436         if (this.resizable) {
22437             this.resizeEl = new Roo.Resizable(this.wrap, {
22438                 pinned : true,
22439                 wrap: true,
22440                 dynamic : true,
22441                 minHeight : this.height,
22442                 height: this.height,
22443                 handles : this.resizable,
22444                 width: this.width,
22445                 listeners : {
22446                     resize : function(r, w, h) {
22447                         _t.onResize(w,h); // -something
22448                     }
22449                 }
22450             });
22451             
22452         }
22453         this.createToolbar(this);
22454        
22455         
22456         if(!this.width && this.resizable){
22457             this.setSize(this.wrap.getSize());
22458         }
22459         if (this.resizeEl) {
22460             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22461             // should trigger onReize..
22462         }
22463         
22464     },
22465
22466     // private
22467     onResize : function(w, h)
22468     {
22469         Roo.log('resize: ' +w + ',' + h );
22470         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22471         var ew = false;
22472         var eh = false;
22473         
22474         if(this.inputEl() ){
22475             if(typeof w == 'number'){
22476                 var aw = w - this.wrap.getFrameWidth('lr');
22477                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22478                 ew = aw;
22479             }
22480             if(typeof h == 'number'){
22481                  var tbh = -11;  // fixme it needs to tool bar size!
22482                 for (var i =0; i < this.toolbars.length;i++) {
22483                     // fixme - ask toolbars for heights?
22484                     tbh += this.toolbars[i].el.getHeight();
22485                     //if (this.toolbars[i].footer) {
22486                     //    tbh += this.toolbars[i].footer.el.getHeight();
22487                     //}
22488                 }
22489               
22490                 
22491                 
22492                 
22493                 
22494                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22495                 ah -= 5; // knock a few pixes off for look..
22496                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
22497                 var eh = ah;
22498             }
22499         }
22500         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22501         this.editorcore.onResize(ew,eh);
22502         
22503     },
22504
22505     /**
22506      * Toggles the editor between standard and source edit mode.
22507      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22508      */
22509     toggleSourceEdit : function(sourceEditMode)
22510     {
22511         this.editorcore.toggleSourceEdit(sourceEditMode);
22512         
22513         if(this.editorcore.sourceEditMode){
22514             Roo.log('editor - showing textarea');
22515             
22516 //            Roo.log('in');
22517 //            Roo.log(this.syncValue());
22518             this.syncValue();
22519             this.inputEl().removeClass(['hide', 'x-hidden']);
22520             this.inputEl().dom.removeAttribute('tabIndex');
22521             this.inputEl().focus();
22522         }else{
22523             Roo.log('editor - hiding textarea');
22524 //            Roo.log('out')
22525 //            Roo.log(this.pushValue()); 
22526             this.pushValue();
22527             
22528             this.inputEl().addClass(['hide', 'x-hidden']);
22529             this.inputEl().dom.setAttribute('tabIndex', -1);
22530             //this.deferFocus();
22531         }
22532          
22533         if(this.resizable){
22534             this.setSize(this.wrap.getSize());
22535         }
22536         
22537         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22538     },
22539  
22540     // private (for BoxComponent)
22541     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22542
22543     // private (for BoxComponent)
22544     getResizeEl : function(){
22545         return this.wrap;
22546     },
22547
22548     // private (for BoxComponent)
22549     getPositionEl : function(){
22550         return this.wrap;
22551     },
22552
22553     // private
22554     initEvents : function(){
22555         this.originalValue = this.getValue();
22556     },
22557
22558 //    /**
22559 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22560 //     * @method
22561 //     */
22562 //    markInvalid : Roo.emptyFn,
22563 //    /**
22564 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22565 //     * @method
22566 //     */
22567 //    clearInvalid : Roo.emptyFn,
22568
22569     setValue : function(v){
22570         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
22571         this.editorcore.pushValue();
22572     },
22573
22574      
22575     // private
22576     deferFocus : function(){
22577         this.focus.defer(10, this);
22578     },
22579
22580     // doc'ed in Field
22581     focus : function(){
22582         this.editorcore.focus();
22583         
22584     },
22585       
22586
22587     // private
22588     onDestroy : function(){
22589         
22590         
22591         
22592         if(this.rendered){
22593             
22594             for (var i =0; i < this.toolbars.length;i++) {
22595                 // fixme - ask toolbars for heights?
22596                 this.toolbars[i].onDestroy();
22597             }
22598             
22599             this.wrap.dom.innerHTML = '';
22600             this.wrap.remove();
22601         }
22602     },
22603
22604     // private
22605     onFirstFocus : function(){
22606         //Roo.log("onFirstFocus");
22607         this.editorcore.onFirstFocus();
22608          for (var i =0; i < this.toolbars.length;i++) {
22609             this.toolbars[i].onFirstFocus();
22610         }
22611         
22612     },
22613     
22614     // private
22615     syncValue : function()
22616     {   
22617         this.editorcore.syncValue();
22618     },
22619     
22620     pushValue : function()
22621     {   
22622         this.editorcore.pushValue();
22623     }
22624      
22625     
22626     // hide stuff that is not compatible
22627     /**
22628      * @event blur
22629      * @hide
22630      */
22631     /**
22632      * @event change
22633      * @hide
22634      */
22635     /**
22636      * @event focus
22637      * @hide
22638      */
22639     /**
22640      * @event specialkey
22641      * @hide
22642      */
22643     /**
22644      * @cfg {String} fieldClass @hide
22645      */
22646     /**
22647      * @cfg {String} focusClass @hide
22648      */
22649     /**
22650      * @cfg {String} autoCreate @hide
22651      */
22652     /**
22653      * @cfg {String} inputType @hide
22654      */
22655     /**
22656      * @cfg {String} invalidClass @hide
22657      */
22658     /**
22659      * @cfg {String} invalidText @hide
22660      */
22661     /**
22662      * @cfg {String} msgFx @hide
22663      */
22664     /**
22665      * @cfg {String} validateOnBlur @hide
22666      */
22667 });
22668  
22669     
22670    
22671    
22672    
22673       
22674 Roo.namespace('Roo.bootstrap.htmleditor');
22675 /**
22676  * @class Roo.bootstrap.HtmlEditorToolbar1
22677  * Basic Toolbar
22678  * 
22679  * Usage:
22680  *
22681  new Roo.bootstrap.HtmlEditor({
22682     ....
22683     toolbars : [
22684         new Roo.bootstrap.HtmlEditorToolbar1({
22685             disable : { fonts: 1 , format: 1, ..., ... , ...],
22686             btns : [ .... ]
22687         })
22688     }
22689      
22690  * 
22691  * @cfg {Object} disable List of elements to disable..
22692  * @cfg {Array} btns List of additional buttons.
22693  * 
22694  * 
22695  * NEEDS Extra CSS? 
22696  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22697  */
22698  
22699 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22700 {
22701     
22702     Roo.apply(this, config);
22703     
22704     // default disabled, based on 'good practice'..
22705     this.disable = this.disable || {};
22706     Roo.applyIf(this.disable, {
22707         fontSize : true,
22708         colors : true,
22709         specialElements : true
22710     });
22711     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22712     
22713     this.editor = config.editor;
22714     this.editorcore = config.editor.editorcore;
22715     
22716     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22717     
22718     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22719     // dont call parent... till later.
22720 }
22721 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
22722      
22723     bar : true,
22724     
22725     editor : false,
22726     editorcore : false,
22727     
22728     
22729     formats : [
22730         "p" ,  
22731         "h1","h2","h3","h4","h5","h6", 
22732         "pre", "code", 
22733         "abbr", "acronym", "address", "cite", "samp", "var",
22734         'div','span'
22735     ],
22736     
22737     onRender : function(ct, position)
22738     {
22739        // Roo.log("Call onRender: " + this.xtype);
22740         
22741        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22742        Roo.log(this.el);
22743        this.el.dom.style.marginBottom = '0';
22744        var _this = this;
22745        var editorcore = this.editorcore;
22746        var editor= this.editor;
22747        
22748        var children = [];
22749        var btn = function(id,cmd , toggle, handler){
22750        
22751             var  event = toggle ? 'toggle' : 'click';
22752        
22753             var a = {
22754                 size : 'sm',
22755                 xtype: 'Button',
22756                 xns: Roo.bootstrap,
22757                 glyphicon : id,
22758                 cmd : id || cmd,
22759                 enableToggle:toggle !== false,
22760                 //html : 'submit'
22761                 pressed : toggle ? false : null,
22762                 listeners : {}
22763             };
22764             a.listeners[toggle ? 'toggle' : 'click'] = function() {
22765                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
22766             };
22767             children.push(a);
22768             return a;
22769        }
22770         
22771         var style = {
22772                 xtype: 'Button',
22773                 size : 'sm',
22774                 xns: Roo.bootstrap,
22775                 glyphicon : 'font',
22776                 //html : 'submit'
22777                 menu : {
22778                     xtype: 'Menu',
22779                     xns: Roo.bootstrap,
22780                     items:  []
22781                 }
22782         };
22783         Roo.each(this.formats, function(f) {
22784             style.menu.items.push({
22785                 xtype :'MenuItem',
22786                 xns: Roo.bootstrap,
22787                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22788                 tagname : f,
22789                 listeners : {
22790                     click : function()
22791                     {
22792                         editorcore.insertTag(this.tagname);
22793                         editor.focus();
22794                     }
22795                 }
22796                 
22797             });
22798         });
22799          children.push(style);   
22800             
22801             
22802         btn('bold',false,true);
22803         btn('italic',false,true);
22804         btn('align-left', 'justifyleft',true);
22805         btn('align-center', 'justifycenter',true);
22806         btn('align-right' , 'justifyright',true);
22807         btn('link', false, false, function(btn) {
22808             //Roo.log("create link?");
22809             var url = prompt(this.createLinkText, this.defaultLinkValue);
22810             if(url && url != 'http:/'+'/'){
22811                 this.editorcore.relayCmd('createlink', url);
22812             }
22813         }),
22814         btn('list','insertunorderedlist',true);
22815         btn('pencil', false,true, function(btn){
22816                 Roo.log(this);
22817                 
22818                 this.toggleSourceEdit(btn.pressed);
22819         });
22820         /*
22821         var cog = {
22822                 xtype: 'Button',
22823                 size : 'sm',
22824                 xns: Roo.bootstrap,
22825                 glyphicon : 'cog',
22826                 //html : 'submit'
22827                 menu : {
22828                     xtype: 'Menu',
22829                     xns: Roo.bootstrap,
22830                     items:  []
22831                 }
22832         };
22833         
22834         cog.menu.items.push({
22835             xtype :'MenuItem',
22836             xns: Roo.bootstrap,
22837             html : Clean styles,
22838             tagname : f,
22839             listeners : {
22840                 click : function()
22841                 {
22842                     editorcore.insertTag(this.tagname);
22843                     editor.focus();
22844                 }
22845             }
22846             
22847         });
22848        */
22849         
22850          
22851        this.xtype = 'NavSimplebar';
22852         
22853         for(var i=0;i< children.length;i++) {
22854             
22855             this.buttons.add(this.addxtypeChild(children[i]));
22856             
22857         }
22858         
22859         editor.on('editorevent', this.updateToolbar, this);
22860     },
22861     onBtnClick : function(id)
22862     {
22863        this.editorcore.relayCmd(id);
22864        this.editorcore.focus();
22865     },
22866     
22867     /**
22868      * Protected method that will not generally be called directly. It triggers
22869      * a toolbar update by reading the markup state of the current selection in the editor.
22870      */
22871     updateToolbar: function(){
22872
22873         if(!this.editorcore.activated){
22874             this.editor.onFirstFocus(); // is this neeed?
22875             return;
22876         }
22877
22878         var btns = this.buttons; 
22879         var doc = this.editorcore.doc;
22880         btns.get('bold').setActive(doc.queryCommandState('bold'));
22881         btns.get('italic').setActive(doc.queryCommandState('italic'));
22882         //btns.get('underline').setActive(doc.queryCommandState('underline'));
22883         
22884         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22885         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22886         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22887         
22888         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22889         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22890          /*
22891         
22892         var ans = this.editorcore.getAllAncestors();
22893         if (this.formatCombo) {
22894             
22895             
22896             var store = this.formatCombo.store;
22897             this.formatCombo.setValue("");
22898             for (var i =0; i < ans.length;i++) {
22899                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22900                     // select it..
22901                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22902                     break;
22903                 }
22904             }
22905         }
22906         
22907         
22908         
22909         // hides menus... - so this cant be on a menu...
22910         Roo.bootstrap.MenuMgr.hideAll();
22911         */
22912         Roo.bootstrap.MenuMgr.hideAll();
22913         //this.editorsyncValue();
22914     },
22915     onFirstFocus: function() {
22916         this.buttons.each(function(item){
22917            item.enable();
22918         });
22919     },
22920     toggleSourceEdit : function(sourceEditMode){
22921         
22922           
22923         if(sourceEditMode){
22924             Roo.log("disabling buttons");
22925            this.buttons.each( function(item){
22926                 if(item.cmd != 'pencil'){
22927                     item.disable();
22928                 }
22929             });
22930           
22931         }else{
22932             Roo.log("enabling buttons");
22933             if(this.editorcore.initialized){
22934                 this.buttons.each( function(item){
22935                     item.enable();
22936                 });
22937             }
22938             
22939         }
22940         Roo.log("calling toggole on editor");
22941         // tell the editor that it's been pressed..
22942         this.editor.toggleSourceEdit(sourceEditMode);
22943        
22944     }
22945 });
22946
22947
22948
22949
22950
22951 /**
22952  * @class Roo.bootstrap.Table.AbstractSelectionModel
22953  * @extends Roo.util.Observable
22954  * Abstract base class for grid SelectionModels.  It provides the interface that should be
22955  * implemented by descendant classes.  This class should not be directly instantiated.
22956  * @constructor
22957  */
22958 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22959     this.locked = false;
22960     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22961 };
22962
22963
22964 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
22965     /** @ignore Called by the grid automatically. Do not call directly. */
22966     init : function(grid){
22967         this.grid = grid;
22968         this.initEvents();
22969     },
22970
22971     /**
22972      * Locks the selections.
22973      */
22974     lock : function(){
22975         this.locked = true;
22976     },
22977
22978     /**
22979      * Unlocks the selections.
22980      */
22981     unlock : function(){
22982         this.locked = false;
22983     },
22984
22985     /**
22986      * Returns true if the selections are locked.
22987      * @return {Boolean}
22988      */
22989     isLocked : function(){
22990         return this.locked;
22991     }
22992 });
22993 /**
22994  * @extends Roo.bootstrap.Table.AbstractSelectionModel
22995  * @class Roo.bootstrap.Table.RowSelectionModel
22996  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22997  * It supports multiple selections and keyboard selection/navigation. 
22998  * @constructor
22999  * @param {Object} config
23000  */
23001
23002 Roo.bootstrap.Table.RowSelectionModel = function(config){
23003     Roo.apply(this, config);
23004     this.selections = new Roo.util.MixedCollection(false, function(o){
23005         return o.id;
23006     });
23007
23008     this.last = false;
23009     this.lastActive = false;
23010
23011     this.addEvents({
23012         /**
23013              * @event selectionchange
23014              * Fires when the selection changes
23015              * @param {SelectionModel} this
23016              */
23017             "selectionchange" : true,
23018         /**
23019              * @event afterselectionchange
23020              * Fires after the selection changes (eg. by key press or clicking)
23021              * @param {SelectionModel} this
23022              */
23023             "afterselectionchange" : true,
23024         /**
23025              * @event beforerowselect
23026              * Fires when a row is selected being selected, return false to cancel.
23027              * @param {SelectionModel} this
23028              * @param {Number} rowIndex The selected index
23029              * @param {Boolean} keepExisting False if other selections will be cleared
23030              */
23031             "beforerowselect" : true,
23032         /**
23033              * @event rowselect
23034              * Fires when a row is selected.
23035              * @param {SelectionModel} this
23036              * @param {Number} rowIndex The selected index
23037              * @param {Roo.data.Record} r The record
23038              */
23039             "rowselect" : true,
23040         /**
23041              * @event rowdeselect
23042              * Fires when a row is deselected.
23043              * @param {SelectionModel} this
23044              * @param {Number} rowIndex The selected index
23045              */
23046         "rowdeselect" : true
23047     });
23048     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23049     this.locked = false;
23050  };
23051
23052 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23053     /**
23054      * @cfg {Boolean} singleSelect
23055      * True to allow selection of only one row at a time (defaults to false)
23056      */
23057     singleSelect : false,
23058
23059     // private
23060     initEvents : function()
23061     {
23062
23063         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23064         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23065         //}else{ // allow click to work like normal
23066          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23067         //}
23068         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23069         this.grid.on("rowclick", this.handleMouseDown, this);
23070         
23071         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23072             "up" : function(e){
23073                 if(!e.shiftKey){
23074                     this.selectPrevious(e.shiftKey);
23075                 }else if(this.last !== false && this.lastActive !== false){
23076                     var last = this.last;
23077                     this.selectRange(this.last,  this.lastActive-1);
23078                     this.grid.getView().focusRow(this.lastActive);
23079                     if(last !== false){
23080                         this.last = last;
23081                     }
23082                 }else{
23083                     this.selectFirstRow();
23084                 }
23085                 this.fireEvent("afterselectionchange", this);
23086             },
23087             "down" : function(e){
23088                 if(!e.shiftKey){
23089                     this.selectNext(e.shiftKey);
23090                 }else if(this.last !== false && this.lastActive !== false){
23091                     var last = this.last;
23092                     this.selectRange(this.last,  this.lastActive+1);
23093                     this.grid.getView().focusRow(this.lastActive);
23094                     if(last !== false){
23095                         this.last = last;
23096                     }
23097                 }else{
23098                     this.selectFirstRow();
23099                 }
23100                 this.fireEvent("afterselectionchange", this);
23101             },
23102             scope: this
23103         });
23104         this.grid.store.on('load', function(){
23105             this.selections.clear();
23106         },this);
23107         /*
23108         var view = this.grid.view;
23109         view.on("refresh", this.onRefresh, this);
23110         view.on("rowupdated", this.onRowUpdated, this);
23111         view.on("rowremoved", this.onRemove, this);
23112         */
23113     },
23114
23115     // private
23116     onRefresh : function()
23117     {
23118         var ds = this.grid.store, i, v = this.grid.view;
23119         var s = this.selections;
23120         s.each(function(r){
23121             if((i = ds.indexOfId(r.id)) != -1){
23122                 v.onRowSelect(i);
23123             }else{
23124                 s.remove(r);
23125             }
23126         });
23127     },
23128
23129     // private
23130     onRemove : function(v, index, r){
23131         this.selections.remove(r);
23132     },
23133
23134     // private
23135     onRowUpdated : function(v, index, r){
23136         if(this.isSelected(r)){
23137             v.onRowSelect(index);
23138         }
23139     },
23140
23141     /**
23142      * Select records.
23143      * @param {Array} records The records to select
23144      * @param {Boolean} keepExisting (optional) True to keep existing selections
23145      */
23146     selectRecords : function(records, keepExisting)
23147     {
23148         if(!keepExisting){
23149             this.clearSelections();
23150         }
23151             var ds = this.grid.store;
23152         for(var i = 0, len = records.length; i < len; i++){
23153             this.selectRow(ds.indexOf(records[i]), true);
23154         }
23155     },
23156
23157     /**
23158      * Gets the number of selected rows.
23159      * @return {Number}
23160      */
23161     getCount : function(){
23162         return this.selections.length;
23163     },
23164
23165     /**
23166      * Selects the first row in the grid.
23167      */
23168     selectFirstRow : function(){
23169         this.selectRow(0);
23170     },
23171
23172     /**
23173      * Select the last row.
23174      * @param {Boolean} keepExisting (optional) True to keep existing selections
23175      */
23176     selectLastRow : function(keepExisting){
23177         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23178         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23179     },
23180
23181     /**
23182      * Selects the row immediately following the last selected row.
23183      * @param {Boolean} keepExisting (optional) True to keep existing selections
23184      */
23185     selectNext : function(keepExisting)
23186     {
23187             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23188             this.selectRow(this.last+1, keepExisting);
23189             this.grid.getView().focusRow(this.last);
23190         }
23191     },
23192
23193     /**
23194      * Selects the row that precedes the last selected row.
23195      * @param {Boolean} keepExisting (optional) True to keep existing selections
23196      */
23197     selectPrevious : function(keepExisting){
23198         if(this.last){
23199             this.selectRow(this.last-1, keepExisting);
23200             this.grid.getView().focusRow(this.last);
23201         }
23202     },
23203
23204     /**
23205      * Returns the selected records
23206      * @return {Array} Array of selected records
23207      */
23208     getSelections : function(){
23209         return [].concat(this.selections.items);
23210     },
23211
23212     /**
23213      * Returns the first selected record.
23214      * @return {Record}
23215      */
23216     getSelected : function(){
23217         return this.selections.itemAt(0);
23218     },
23219
23220
23221     /**
23222      * Clears all selections.
23223      */
23224     clearSelections : function(fast)
23225     {
23226         if(this.locked) {
23227             return;
23228         }
23229         if(fast !== true){
23230                 var ds = this.grid.store;
23231             var s = this.selections;
23232             s.each(function(r){
23233                 this.deselectRow(ds.indexOfId(r.id));
23234             }, this);
23235             s.clear();
23236         }else{
23237             this.selections.clear();
23238         }
23239         this.last = false;
23240     },
23241
23242
23243     /**
23244      * Selects all rows.
23245      */
23246     selectAll : function(){
23247         if(this.locked) {
23248             return;
23249         }
23250         this.selections.clear();
23251         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23252             this.selectRow(i, true);
23253         }
23254     },
23255
23256     /**
23257      * Returns True if there is a selection.
23258      * @return {Boolean}
23259      */
23260     hasSelection : function(){
23261         return this.selections.length > 0;
23262     },
23263
23264     /**
23265      * Returns True if the specified row is selected.
23266      * @param {Number/Record} record The record or index of the record to check
23267      * @return {Boolean}
23268      */
23269     isSelected : function(index){
23270             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23271         return (r && this.selections.key(r.id) ? true : false);
23272     },
23273
23274     /**
23275      * Returns True if the specified record id is selected.
23276      * @param {String} id The id of record to check
23277      * @return {Boolean}
23278      */
23279     isIdSelected : function(id){
23280         return (this.selections.key(id) ? true : false);
23281     },
23282
23283
23284     // private
23285     handleMouseDBClick : function(e, t){
23286         
23287     },
23288     // private
23289     handleMouseDown : function(e, t)
23290     {
23291             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23292         if(this.isLocked() || rowIndex < 0 ){
23293             return;
23294         };
23295         if(e.shiftKey && this.last !== false){
23296             var last = this.last;
23297             this.selectRange(last, rowIndex, e.ctrlKey);
23298             this.last = last; // reset the last
23299             t.focus();
23300     
23301         }else{
23302             var isSelected = this.isSelected(rowIndex);
23303             //Roo.log("select row:" + rowIndex);
23304             if(isSelected){
23305                 this.deselectRow(rowIndex);
23306             } else {
23307                         this.selectRow(rowIndex, true);
23308             }
23309     
23310             /*
23311                 if(e.button !== 0 && isSelected){
23312                 alert('rowIndex 2: ' + rowIndex);
23313                     view.focusRow(rowIndex);
23314                 }else if(e.ctrlKey && isSelected){
23315                     this.deselectRow(rowIndex);
23316                 }else if(!isSelected){
23317                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23318                     view.focusRow(rowIndex);
23319                 }
23320             */
23321         }
23322         this.fireEvent("afterselectionchange", this);
23323     },
23324     // private
23325     handleDragableRowClick :  function(grid, rowIndex, e) 
23326     {
23327         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23328             this.selectRow(rowIndex, false);
23329             grid.view.focusRow(rowIndex);
23330              this.fireEvent("afterselectionchange", this);
23331         }
23332     },
23333     
23334     /**
23335      * Selects multiple rows.
23336      * @param {Array} rows Array of the indexes of the row to select
23337      * @param {Boolean} keepExisting (optional) True to keep existing selections
23338      */
23339     selectRows : function(rows, keepExisting){
23340         if(!keepExisting){
23341             this.clearSelections();
23342         }
23343         for(var i = 0, len = rows.length; i < len; i++){
23344             this.selectRow(rows[i], true);
23345         }
23346     },
23347
23348     /**
23349      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23350      * @param {Number} startRow The index of the first row in the range
23351      * @param {Number} endRow The index of the last row in the range
23352      * @param {Boolean} keepExisting (optional) True to retain existing selections
23353      */
23354     selectRange : function(startRow, endRow, keepExisting){
23355         if(this.locked) {
23356             return;
23357         }
23358         if(!keepExisting){
23359             this.clearSelections();
23360         }
23361         if(startRow <= endRow){
23362             for(var i = startRow; i <= endRow; i++){
23363                 this.selectRow(i, true);
23364             }
23365         }else{
23366             for(var i = startRow; i >= endRow; i--){
23367                 this.selectRow(i, true);
23368             }
23369         }
23370     },
23371
23372     /**
23373      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23374      * @param {Number} startRow The index of the first row in the range
23375      * @param {Number} endRow The index of the last row in the range
23376      */
23377     deselectRange : function(startRow, endRow, preventViewNotify){
23378         if(this.locked) {
23379             return;
23380         }
23381         for(var i = startRow; i <= endRow; i++){
23382             this.deselectRow(i, preventViewNotify);
23383         }
23384     },
23385
23386     /**
23387      * Selects a row.
23388      * @param {Number} row The index of the row to select
23389      * @param {Boolean} keepExisting (optional) True to keep existing selections
23390      */
23391     selectRow : function(index, keepExisting, preventViewNotify)
23392     {
23393             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23394             return;
23395         }
23396         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23397             if(!keepExisting || this.singleSelect){
23398                 this.clearSelections();
23399             }
23400             
23401             var r = this.grid.store.getAt(index);
23402             //console.log('selectRow - record id :' + r.id);
23403             
23404             this.selections.add(r);
23405             this.last = this.lastActive = index;
23406             if(!preventViewNotify){
23407                 var proxy = new Roo.Element(
23408                                 this.grid.getRowDom(index)
23409                 );
23410                 proxy.addClass('bg-info info');
23411             }
23412             this.fireEvent("rowselect", this, index, r);
23413             this.fireEvent("selectionchange", this);
23414         }
23415     },
23416
23417     /**
23418      * Deselects a row.
23419      * @param {Number} row The index of the row to deselect
23420      */
23421     deselectRow : function(index, preventViewNotify)
23422     {
23423         if(this.locked) {
23424             return;
23425         }
23426         if(this.last == index){
23427             this.last = false;
23428         }
23429         if(this.lastActive == index){
23430             this.lastActive = false;
23431         }
23432         
23433         var r = this.grid.store.getAt(index);
23434         if (!r) {
23435             return;
23436         }
23437         
23438         this.selections.remove(r);
23439         //.console.log('deselectRow - record id :' + r.id);
23440         if(!preventViewNotify){
23441         
23442             var proxy = new Roo.Element(
23443                 this.grid.getRowDom(index)
23444             );
23445             proxy.removeClass('bg-info info');
23446         }
23447         this.fireEvent("rowdeselect", this, index);
23448         this.fireEvent("selectionchange", this);
23449     },
23450
23451     // private
23452     restoreLast : function(){
23453         if(this._last){
23454             this.last = this._last;
23455         }
23456     },
23457
23458     // private
23459     acceptsNav : function(row, col, cm){
23460         return !cm.isHidden(col) && cm.isCellEditable(col, row);
23461     },
23462
23463     // private
23464     onEditorKey : function(field, e){
23465         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23466         if(k == e.TAB){
23467             e.stopEvent();
23468             ed.completeEdit();
23469             if(e.shiftKey){
23470                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23471             }else{
23472                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23473             }
23474         }else if(k == e.ENTER && !e.ctrlKey){
23475             e.stopEvent();
23476             ed.completeEdit();
23477             if(e.shiftKey){
23478                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
23479             }else{
23480                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
23481             }
23482         }else if(k == e.ESC){
23483             ed.cancelEdit();
23484         }
23485         if(newCell){
23486             g.startEditing(newCell[0], newCell[1]);
23487         }
23488     }
23489 });
23490 /*
23491  * Based on:
23492  * Ext JS Library 1.1.1
23493  * Copyright(c) 2006-2007, Ext JS, LLC.
23494  *
23495  * Originally Released Under LGPL - original licence link has changed is not relivant.
23496  *
23497  * Fork - LGPL
23498  * <script type="text/javascript">
23499  */
23500  
23501 /**
23502  * @class Roo.bootstrap.PagingToolbar
23503  * @extends Roo.bootstrap.NavSimplebar
23504  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
23505  * @constructor
23506  * Create a new PagingToolbar
23507  * @param {Object} config The config object
23508  * @param {Roo.data.Store} store
23509  */
23510 Roo.bootstrap.PagingToolbar = function(config)
23511 {
23512     // old args format still supported... - xtype is prefered..
23513         // created from xtype...
23514     
23515     this.ds = config.dataSource;
23516     
23517     if (config.store && !this.ds) {
23518         this.store= Roo.factory(config.store, Roo.data);
23519         this.ds = this.store;
23520         this.ds.xmodule = this.xmodule || false;
23521     }
23522     
23523     this.toolbarItems = [];
23524     if (config.items) {
23525         this.toolbarItems = config.items;
23526     }
23527     
23528     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
23529     
23530     this.cursor = 0;
23531     
23532     if (this.ds) { 
23533         this.bind(this.ds);
23534     }
23535     
23536     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
23537     
23538 };
23539
23540 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
23541     /**
23542      * @cfg {Roo.data.Store} dataSource
23543      * The underlying data store providing the paged data
23544      */
23545     /**
23546      * @cfg {String/HTMLElement/Element} container
23547      * container The id or element that will contain the toolbar
23548      */
23549     /**
23550      * @cfg {Boolean} displayInfo
23551      * True to display the displayMsg (defaults to false)
23552      */
23553     /**
23554      * @cfg {Number} pageSize
23555      * The number of records to display per page (defaults to 20)
23556      */
23557     pageSize: 20,
23558     /**
23559      * @cfg {String} displayMsg
23560      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
23561      */
23562     displayMsg : 'Displaying {0} - {1} of {2}',
23563     /**
23564      * @cfg {String} emptyMsg
23565      * The message to display when no records are found (defaults to "No data to display")
23566      */
23567     emptyMsg : 'No data to display',
23568     /**
23569      * Customizable piece of the default paging text (defaults to "Page")
23570      * @type String
23571      */
23572     beforePageText : "Page",
23573     /**
23574      * Customizable piece of the default paging text (defaults to "of %0")
23575      * @type String
23576      */
23577     afterPageText : "of {0}",
23578     /**
23579      * Customizable piece of the default paging text (defaults to "First Page")
23580      * @type String
23581      */
23582     firstText : "First Page",
23583     /**
23584      * Customizable piece of the default paging text (defaults to "Previous Page")
23585      * @type String
23586      */
23587     prevText : "Previous Page",
23588     /**
23589      * Customizable piece of the default paging text (defaults to "Next Page")
23590      * @type String
23591      */
23592     nextText : "Next Page",
23593     /**
23594      * Customizable piece of the default paging text (defaults to "Last Page")
23595      * @type String
23596      */
23597     lastText : "Last Page",
23598     /**
23599      * Customizable piece of the default paging text (defaults to "Refresh")
23600      * @type String
23601      */
23602     refreshText : "Refresh",
23603
23604     buttons : false,
23605     // private
23606     onRender : function(ct, position) 
23607     {
23608         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
23609         this.navgroup.parentId = this.id;
23610         this.navgroup.onRender(this.el, null);
23611         // add the buttons to the navgroup
23612         
23613         if(this.displayInfo){
23614             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
23615             this.displayEl = this.el.select('.x-paging-info', true).first();
23616 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
23617 //            this.displayEl = navel.el.select('span',true).first();
23618         }
23619         
23620         var _this = this;
23621         
23622         if(this.buttons){
23623             Roo.each(_this.buttons, function(e){ // this might need to use render????
23624                Roo.factory(e).onRender(_this.el, null);
23625             });
23626         }
23627             
23628         Roo.each(_this.toolbarItems, function(e) {
23629             _this.navgroup.addItem(e);
23630         });
23631         
23632         
23633         this.first = this.navgroup.addItem({
23634             tooltip: this.firstText,
23635             cls: "prev",
23636             icon : 'fa fa-backward',
23637             disabled: true,
23638             preventDefault: true,
23639             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23640         });
23641         
23642         this.prev =  this.navgroup.addItem({
23643             tooltip: this.prevText,
23644             cls: "prev",
23645             icon : 'fa fa-step-backward',
23646             disabled: true,
23647             preventDefault: true,
23648             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
23649         });
23650     //this.addSeparator();
23651         
23652         
23653         var field = this.navgroup.addItem( {
23654             tagtype : 'span',
23655             cls : 'x-paging-position',
23656             
23657             html : this.beforePageText  +
23658                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23659                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
23660          } ); //?? escaped?
23661         
23662         this.field = field.el.select('input', true).first();
23663         this.field.on("keydown", this.onPagingKeydown, this);
23664         this.field.on("focus", function(){this.dom.select();});
23665     
23666     
23667         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
23668         //this.field.setHeight(18);
23669         //this.addSeparator();
23670         this.next = this.navgroup.addItem({
23671             tooltip: this.nextText,
23672             cls: "next",
23673             html : ' <i class="fa fa-step-forward">',
23674             disabled: true,
23675             preventDefault: true,
23676             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
23677         });
23678         this.last = this.navgroup.addItem({
23679             tooltip: this.lastText,
23680             icon : 'fa fa-forward',
23681             cls: "next",
23682             disabled: true,
23683             preventDefault: true,
23684             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
23685         });
23686     //this.addSeparator();
23687         this.loading = this.navgroup.addItem({
23688             tooltip: this.refreshText,
23689             icon: 'fa fa-refresh',
23690             preventDefault: true,
23691             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23692         });
23693         
23694     },
23695
23696     // private
23697     updateInfo : function(){
23698         if(this.displayEl){
23699             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23700             var msg = count == 0 ?
23701                 this.emptyMsg :
23702                 String.format(
23703                     this.displayMsg,
23704                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
23705                 );
23706             this.displayEl.update(msg);
23707         }
23708     },
23709
23710     // private
23711     onLoad : function(ds, r, o){
23712        this.cursor = o.params ? o.params.start : 0;
23713        var d = this.getPageData(),
23714             ap = d.activePage,
23715             ps = d.pages;
23716         
23717        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23718        this.field.dom.value = ap;
23719        this.first.setDisabled(ap == 1);
23720        this.prev.setDisabled(ap == 1);
23721        this.next.setDisabled(ap == ps);
23722        this.last.setDisabled(ap == ps);
23723        this.loading.enable();
23724        this.updateInfo();
23725     },
23726
23727     // private
23728     getPageData : function(){
23729         var total = this.ds.getTotalCount();
23730         return {
23731             total : total,
23732             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23733             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23734         };
23735     },
23736
23737     // private
23738     onLoadError : function(){
23739         this.loading.enable();
23740     },
23741
23742     // private
23743     onPagingKeydown : function(e){
23744         var k = e.getKey();
23745         var d = this.getPageData();
23746         if(k == e.RETURN){
23747             var v = this.field.dom.value, pageNum;
23748             if(!v || isNaN(pageNum = parseInt(v, 10))){
23749                 this.field.dom.value = d.activePage;
23750                 return;
23751             }
23752             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23753             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23754             e.stopEvent();
23755         }
23756         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))
23757         {
23758           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23759           this.field.dom.value = pageNum;
23760           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23761           e.stopEvent();
23762         }
23763         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23764         {
23765           var v = this.field.dom.value, pageNum; 
23766           var increment = (e.shiftKey) ? 10 : 1;
23767           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23768                 increment *= -1;
23769           }
23770           if(!v || isNaN(pageNum = parseInt(v, 10))) {
23771             this.field.dom.value = d.activePage;
23772             return;
23773           }
23774           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23775           {
23776             this.field.dom.value = parseInt(v, 10) + increment;
23777             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23778             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23779           }
23780           e.stopEvent();
23781         }
23782     },
23783
23784     // private
23785     beforeLoad : function(){
23786         if(this.loading){
23787             this.loading.disable();
23788         }
23789     },
23790
23791     // private
23792     onClick : function(which){
23793         
23794         var ds = this.ds;
23795         if (!ds) {
23796             return;
23797         }
23798         
23799         switch(which){
23800             case "first":
23801                 ds.load({params:{start: 0, limit: this.pageSize}});
23802             break;
23803             case "prev":
23804                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23805             break;
23806             case "next":
23807                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23808             break;
23809             case "last":
23810                 var total = ds.getTotalCount();
23811                 var extra = total % this.pageSize;
23812                 var lastStart = extra ? (total - extra) : total-this.pageSize;
23813                 ds.load({params:{start: lastStart, limit: this.pageSize}});
23814             break;
23815             case "refresh":
23816                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23817             break;
23818         }
23819     },
23820
23821     /**
23822      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23823      * @param {Roo.data.Store} store The data store to unbind
23824      */
23825     unbind : function(ds){
23826         ds.un("beforeload", this.beforeLoad, this);
23827         ds.un("load", this.onLoad, this);
23828         ds.un("loadexception", this.onLoadError, this);
23829         ds.un("remove", this.updateInfo, this);
23830         ds.un("add", this.updateInfo, this);
23831         this.ds = undefined;
23832     },
23833
23834     /**
23835      * Binds the paging toolbar to the specified {@link Roo.data.Store}
23836      * @param {Roo.data.Store} store The data store to bind
23837      */
23838     bind : function(ds){
23839         ds.on("beforeload", this.beforeLoad, this);
23840         ds.on("load", this.onLoad, this);
23841         ds.on("loadexception", this.onLoadError, this);
23842         ds.on("remove", this.updateInfo, this);
23843         ds.on("add", this.updateInfo, this);
23844         this.ds = ds;
23845     }
23846 });/*
23847  * - LGPL
23848  *
23849  * element
23850  * 
23851  */
23852
23853 /**
23854  * @class Roo.bootstrap.MessageBar
23855  * @extends Roo.bootstrap.Component
23856  * Bootstrap MessageBar class
23857  * @cfg {String} html contents of the MessageBar
23858  * @cfg {String} weight (info | success | warning | danger) default info
23859  * @cfg {String} beforeClass insert the bar before the given class
23860  * @cfg {Boolean} closable (true | false) default false
23861  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23862  * 
23863  * @constructor
23864  * Create a new Element
23865  * @param {Object} config The config object
23866  */
23867
23868 Roo.bootstrap.MessageBar = function(config){
23869     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23870 };
23871
23872 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
23873     
23874     html: '',
23875     weight: 'info',
23876     closable: false,
23877     fixed: false,
23878     beforeClass: 'bootstrap-sticky-wrap',
23879     
23880     getAutoCreate : function(){
23881         
23882         var cfg = {
23883             tag: 'div',
23884             cls: 'alert alert-dismissable alert-' + this.weight,
23885             cn: [
23886                 {
23887                     tag: 'span',
23888                     cls: 'message',
23889                     html: this.html || ''
23890                 }
23891             ]
23892         };
23893         
23894         if(this.fixed){
23895             cfg.cls += ' alert-messages-fixed';
23896         }
23897         
23898         if(this.closable){
23899             cfg.cn.push({
23900                 tag: 'button',
23901                 cls: 'close',
23902                 html: 'x'
23903             });
23904         }
23905         
23906         return cfg;
23907     },
23908     
23909     onRender : function(ct, position)
23910     {
23911         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23912         
23913         if(!this.el){
23914             var cfg = Roo.apply({},  this.getAutoCreate());
23915             cfg.id = Roo.id();
23916             
23917             if (this.cls) {
23918                 cfg.cls += ' ' + this.cls;
23919             }
23920             if (this.style) {
23921                 cfg.style = this.style;
23922             }
23923             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23924             
23925             this.el.setVisibilityMode(Roo.Element.DISPLAY);
23926         }
23927         
23928         this.el.select('>button.close').on('click', this.hide, this);
23929         
23930     },
23931     
23932     show : function()
23933     {
23934         if (!this.rendered) {
23935             this.render();
23936         }
23937         
23938         this.el.show();
23939         
23940         this.fireEvent('show', this);
23941         
23942     },
23943     
23944     hide : function()
23945     {
23946         if (!this.rendered) {
23947             this.render();
23948         }
23949         
23950         this.el.hide();
23951         
23952         this.fireEvent('hide', this);
23953     },
23954     
23955     update : function()
23956     {
23957 //        var e = this.el.dom.firstChild;
23958 //        
23959 //        if(this.closable){
23960 //            e = e.nextSibling;
23961 //        }
23962 //        
23963 //        e.data = this.html || '';
23964
23965         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23966     }
23967    
23968 });
23969
23970  
23971
23972      /*
23973  * - LGPL
23974  *
23975  * Graph
23976  * 
23977  */
23978
23979
23980 /**
23981  * @class Roo.bootstrap.Graph
23982  * @extends Roo.bootstrap.Component
23983  * Bootstrap Graph class
23984 > Prameters
23985  -sm {number} sm 4
23986  -md {number} md 5
23987  @cfg {String} graphtype  bar | vbar | pie
23988  @cfg {number} g_x coodinator | centre x (pie)
23989  @cfg {number} g_y coodinator | centre y (pie)
23990  @cfg {number} g_r radius (pie)
23991  @cfg {number} g_height height of the chart (respected by all elements in the set)
23992  @cfg {number} g_width width of the chart (respected by all elements in the set)
23993  @cfg {Object} title The title of the chart
23994     
23995  -{Array}  values
23996  -opts (object) options for the chart 
23997      o {
23998      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23999      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24000      o vgutter (number)
24001      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.
24002      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24003      o to
24004      o stretch (boolean)
24005      o }
24006  -opts (object) options for the pie
24007      o{
24008      o cut
24009      o startAngle (number)
24010      o endAngle (number)
24011      } 
24012  *
24013  * @constructor
24014  * Create a new Input
24015  * @param {Object} config The config object
24016  */
24017
24018 Roo.bootstrap.Graph = function(config){
24019     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24020     
24021     this.addEvents({
24022         // img events
24023         /**
24024          * @event click
24025          * The img click event for the img.
24026          * @param {Roo.EventObject} e
24027          */
24028         "click" : true
24029     });
24030 };
24031
24032 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24033     
24034     sm: 4,
24035     md: 5,
24036     graphtype: 'bar',
24037     g_height: 250,
24038     g_width: 400,
24039     g_x: 50,
24040     g_y: 50,
24041     g_r: 30,
24042     opts:{
24043         //g_colors: this.colors,
24044         g_type: 'soft',
24045         g_gutter: '20%'
24046
24047     },
24048     title : false,
24049
24050     getAutoCreate : function(){
24051         
24052         var cfg = {
24053             tag: 'div',
24054             html : null
24055         };
24056         
24057         
24058         return  cfg;
24059     },
24060
24061     onRender : function(ct,position){
24062         
24063         
24064         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24065         
24066         if (typeof(Raphael) == 'undefined') {
24067             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24068             return;
24069         }
24070         
24071         this.raphael = Raphael(this.el.dom);
24072         
24073                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24074                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24075                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24076                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24077                 /*
24078                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24079                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24080                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24081                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24082                 
24083                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24084                 r.barchart(330, 10, 300, 220, data1);
24085                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24086                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24087                 */
24088                 
24089                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24090                 // r.barchart(30, 30, 560, 250,  xdata, {
24091                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24092                 //     axis : "0 0 1 1",
24093                 //     axisxlabels :  xdata
24094                 //     //yvalues : cols,
24095                    
24096                 // });
24097 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24098 //        
24099 //        this.load(null,xdata,{
24100 //                axis : "0 0 1 1",
24101 //                axisxlabels :  xdata
24102 //                });
24103
24104     },
24105
24106     load : function(graphtype,xdata,opts)
24107     {
24108         this.raphael.clear();
24109         if(!graphtype) {
24110             graphtype = this.graphtype;
24111         }
24112         if(!opts){
24113             opts = this.opts;
24114         }
24115         var r = this.raphael,
24116             fin = function () {
24117                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24118             },
24119             fout = function () {
24120                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24121             },
24122             pfin = function() {
24123                 this.sector.stop();
24124                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24125
24126                 if (this.label) {
24127                     this.label[0].stop();
24128                     this.label[0].attr({ r: 7.5 });
24129                     this.label[1].attr({ "font-weight": 800 });
24130                 }
24131             },
24132             pfout = function() {
24133                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24134
24135                 if (this.label) {
24136                     this.label[0].animate({ r: 5 }, 500, "bounce");
24137                     this.label[1].attr({ "font-weight": 400 });
24138                 }
24139             };
24140
24141         switch(graphtype){
24142             case 'bar':
24143                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24144                 break;
24145             case 'hbar':
24146                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24147                 break;
24148             case 'pie':
24149 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24150 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24151 //            
24152                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24153                 
24154                 break;
24155
24156         }
24157         
24158         if(this.title){
24159             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24160         }
24161         
24162     },
24163     
24164     setTitle: function(o)
24165     {
24166         this.title = o;
24167     },
24168     
24169     initEvents: function() {
24170         
24171         if(!this.href){
24172             this.el.on('click', this.onClick, this);
24173         }
24174     },
24175     
24176     onClick : function(e)
24177     {
24178         Roo.log('img onclick');
24179         this.fireEvent('click', this, e);
24180     }
24181    
24182 });
24183
24184  
24185 /*
24186  * - LGPL
24187  *
24188  * numberBox
24189  * 
24190  */
24191 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24192
24193 /**
24194  * @class Roo.bootstrap.dash.NumberBox
24195  * @extends Roo.bootstrap.Component
24196  * Bootstrap NumberBox class
24197  * @cfg {String} headline Box headline
24198  * @cfg {String} content Box content
24199  * @cfg {String} icon Box icon
24200  * @cfg {String} footer Footer text
24201  * @cfg {String} fhref Footer href
24202  * 
24203  * @constructor
24204  * Create a new NumberBox
24205  * @param {Object} config The config object
24206  */
24207
24208
24209 Roo.bootstrap.dash.NumberBox = function(config){
24210     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24211     
24212 };
24213
24214 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24215     
24216     headline : '',
24217     content : '',
24218     icon : '',
24219     footer : '',
24220     fhref : '',
24221     ficon : '',
24222     
24223     getAutoCreate : function(){
24224         
24225         var cfg = {
24226             tag : 'div',
24227             cls : 'small-box ',
24228             cn : [
24229                 {
24230                     tag : 'div',
24231                     cls : 'inner',
24232                     cn :[
24233                         {
24234                             tag : 'h3',
24235                             cls : 'roo-headline',
24236                             html : this.headline
24237                         },
24238                         {
24239                             tag : 'p',
24240                             cls : 'roo-content',
24241                             html : this.content
24242                         }
24243                     ]
24244                 }
24245             ]
24246         };
24247         
24248         if(this.icon){
24249             cfg.cn.push({
24250                 tag : 'div',
24251                 cls : 'icon',
24252                 cn :[
24253                     {
24254                         tag : 'i',
24255                         cls : 'ion ' + this.icon
24256                     }
24257                 ]
24258             });
24259         }
24260         
24261         if(this.footer){
24262             var footer = {
24263                 tag : 'a',
24264                 cls : 'small-box-footer',
24265                 href : this.fhref || '#',
24266                 html : this.footer
24267             };
24268             
24269             cfg.cn.push(footer);
24270             
24271         }
24272         
24273         return  cfg;
24274     },
24275
24276     onRender : function(ct,position){
24277         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24278
24279
24280        
24281                 
24282     },
24283
24284     setHeadline: function (value)
24285     {
24286         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24287     },
24288     
24289     setFooter: function (value, href)
24290     {
24291         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24292         
24293         if(href){
24294             this.el.select('a.small-box-footer',true).first().attr('href', href);
24295         }
24296         
24297     },
24298
24299     setContent: function (value)
24300     {
24301         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24302     },
24303
24304     initEvents: function() 
24305     {   
24306         
24307     }
24308     
24309 });
24310
24311  
24312 /*
24313  * - LGPL
24314  *
24315  * TabBox
24316  * 
24317  */
24318 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24319
24320 /**
24321  * @class Roo.bootstrap.dash.TabBox
24322  * @extends Roo.bootstrap.Component
24323  * Bootstrap TabBox class
24324  * @cfg {String} title Title of the TabBox
24325  * @cfg {String} icon Icon of the TabBox
24326  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24327  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24328  * 
24329  * @constructor
24330  * Create a new TabBox
24331  * @param {Object} config The config object
24332  */
24333
24334
24335 Roo.bootstrap.dash.TabBox = function(config){
24336     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24337     this.addEvents({
24338         // raw events
24339         /**
24340          * @event addpane
24341          * When a pane is added
24342          * @param {Roo.bootstrap.dash.TabPane} pane
24343          */
24344         "addpane" : true,
24345         /**
24346          * @event activatepane
24347          * When a pane is activated
24348          * @param {Roo.bootstrap.dash.TabPane} pane
24349          */
24350         "activatepane" : true
24351         
24352          
24353     });
24354     
24355     this.panes = [];
24356 };
24357
24358 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24359
24360     title : '',
24361     icon : false,
24362     showtabs : true,
24363     tabScrollable : false,
24364     
24365     getChildContainer : function()
24366     {
24367         return this.el.select('.tab-content', true).first();
24368     },
24369     
24370     getAutoCreate : function(){
24371         
24372         var header = {
24373             tag: 'li',
24374             cls: 'pull-left header',
24375             html: this.title,
24376             cn : []
24377         };
24378         
24379         if(this.icon){
24380             header.cn.push({
24381                 tag: 'i',
24382                 cls: 'fa ' + this.icon
24383             });
24384         }
24385         
24386         var h = {
24387             tag: 'ul',
24388             cls: 'nav nav-tabs pull-right',
24389             cn: [
24390                 header
24391             ]
24392         };
24393         
24394         if(this.tabScrollable){
24395             h = {
24396                 tag: 'div',
24397                 cls: 'tab-header',
24398                 cn: [
24399                     {
24400                         tag: 'ul',
24401                         cls: 'nav nav-tabs pull-right',
24402                         cn: [
24403                             header
24404                         ]
24405                     }
24406                 ]
24407             };
24408         }
24409         
24410         var cfg = {
24411             tag: 'div',
24412             cls: 'nav-tabs-custom',
24413             cn: [
24414                 h,
24415                 {
24416                     tag: 'div',
24417                     cls: 'tab-content no-padding',
24418                     cn: []
24419                 }
24420             ]
24421         };
24422
24423         return  cfg;
24424     },
24425     initEvents : function()
24426     {
24427         //Roo.log('add add pane handler');
24428         this.on('addpane', this.onAddPane, this);
24429     },
24430      /**
24431      * Updates the box title
24432      * @param {String} html to set the title to.
24433      */
24434     setTitle : function(value)
24435     {
24436         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24437     },
24438     onAddPane : function(pane)
24439     {
24440         this.panes.push(pane);
24441         //Roo.log('addpane');
24442         //Roo.log(pane);
24443         // tabs are rendere left to right..
24444         if(!this.showtabs){
24445             return;
24446         }
24447         
24448         var ctr = this.el.select('.nav-tabs', true).first();
24449          
24450          
24451         var existing = ctr.select('.nav-tab',true);
24452         var qty = existing.getCount();;
24453         
24454         
24455         var tab = ctr.createChild({
24456             tag : 'li',
24457             cls : 'nav-tab' + (qty ? '' : ' active'),
24458             cn : [
24459                 {
24460                     tag : 'a',
24461                     href:'#',
24462                     html : pane.title
24463                 }
24464             ]
24465         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24466         pane.tab = tab;
24467         
24468         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24469         if (!qty) {
24470             pane.el.addClass('active');
24471         }
24472         
24473                 
24474     },
24475     onTabClick : function(ev,un,ob,pane)
24476     {
24477         //Roo.log('tab - prev default');
24478         ev.preventDefault();
24479         
24480         
24481         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
24482         pane.tab.addClass('active');
24483         //Roo.log(pane.title);
24484         this.getChildContainer().select('.tab-pane',true).removeClass('active');
24485         // technically we should have a deactivate event.. but maybe add later.
24486         // and it should not de-activate the selected tab...
24487         this.fireEvent('activatepane', pane);
24488         pane.el.addClass('active');
24489         pane.fireEvent('activate');
24490         
24491         
24492     },
24493     
24494     getActivePane : function()
24495     {
24496         var r = false;
24497         Roo.each(this.panes, function(p) {
24498             if(p.el.hasClass('active')){
24499                 r = p;
24500                 return false;
24501             }
24502             
24503             return;
24504         });
24505         
24506         return r;
24507     }
24508     
24509     
24510 });
24511
24512  
24513 /*
24514  * - LGPL
24515  *
24516  * Tab pane
24517  * 
24518  */
24519 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24520 /**
24521  * @class Roo.bootstrap.TabPane
24522  * @extends Roo.bootstrap.Component
24523  * Bootstrap TabPane class
24524  * @cfg {Boolean} active (false | true) Default false
24525  * @cfg {String} title title of panel
24526
24527  * 
24528  * @constructor
24529  * Create a new TabPane
24530  * @param {Object} config The config object
24531  */
24532
24533 Roo.bootstrap.dash.TabPane = function(config){
24534     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
24535     
24536     this.addEvents({
24537         // raw events
24538         /**
24539          * @event activate
24540          * When a pane is activated
24541          * @param {Roo.bootstrap.dash.TabPane} pane
24542          */
24543         "activate" : true
24544          
24545     });
24546 };
24547
24548 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
24549     
24550     active : false,
24551     title : '',
24552     
24553     // the tabBox that this is attached to.
24554     tab : false,
24555      
24556     getAutoCreate : function() 
24557     {
24558         var cfg = {
24559             tag: 'div',
24560             cls: 'tab-pane'
24561         };
24562         
24563         if(this.active){
24564             cfg.cls += ' active';
24565         }
24566         
24567         return cfg;
24568     },
24569     initEvents  : function()
24570     {
24571         //Roo.log('trigger add pane handler');
24572         this.parent().fireEvent('addpane', this)
24573     },
24574     
24575      /**
24576      * Updates the tab title 
24577      * @param {String} html to set the title to.
24578      */
24579     setTitle: function(str)
24580     {
24581         if (!this.tab) {
24582             return;
24583         }
24584         this.title = str;
24585         this.tab.select('a', true).first().dom.innerHTML = str;
24586         
24587     }
24588     
24589     
24590     
24591 });
24592
24593  
24594
24595
24596  /*
24597  * - LGPL
24598  *
24599  * menu
24600  * 
24601  */
24602 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24603
24604 /**
24605  * @class Roo.bootstrap.menu.Menu
24606  * @extends Roo.bootstrap.Component
24607  * Bootstrap Menu class - container for Menu
24608  * @cfg {String} html Text of the menu
24609  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
24610  * @cfg {String} icon Font awesome icon
24611  * @cfg {String} pos Menu align to (top | bottom) default bottom
24612  * 
24613  * 
24614  * @constructor
24615  * Create a new Menu
24616  * @param {Object} config The config object
24617  */
24618
24619
24620 Roo.bootstrap.menu.Menu = function(config){
24621     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
24622     
24623     this.addEvents({
24624         /**
24625          * @event beforeshow
24626          * Fires before this menu is displayed
24627          * @param {Roo.bootstrap.menu.Menu} this
24628          */
24629         beforeshow : true,
24630         /**
24631          * @event beforehide
24632          * Fires before this menu is hidden
24633          * @param {Roo.bootstrap.menu.Menu} this
24634          */
24635         beforehide : true,
24636         /**
24637          * @event show
24638          * Fires after this menu is displayed
24639          * @param {Roo.bootstrap.menu.Menu} this
24640          */
24641         show : true,
24642         /**
24643          * @event hide
24644          * Fires after this menu is hidden
24645          * @param {Roo.bootstrap.menu.Menu} this
24646          */
24647         hide : true,
24648         /**
24649          * @event click
24650          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24651          * @param {Roo.bootstrap.menu.Menu} this
24652          * @param {Roo.EventObject} e
24653          */
24654         click : true
24655     });
24656     
24657 };
24658
24659 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
24660     
24661     submenu : false,
24662     html : '',
24663     weight : 'default',
24664     icon : false,
24665     pos : 'bottom',
24666     
24667     
24668     getChildContainer : function() {
24669         if(this.isSubMenu){
24670             return this.el;
24671         }
24672         
24673         return this.el.select('ul.dropdown-menu', true).first();  
24674     },
24675     
24676     getAutoCreate : function()
24677     {
24678         var text = [
24679             {
24680                 tag : 'span',
24681                 cls : 'roo-menu-text',
24682                 html : this.html
24683             }
24684         ];
24685         
24686         if(this.icon){
24687             text.unshift({
24688                 tag : 'i',
24689                 cls : 'fa ' + this.icon
24690             })
24691         }
24692         
24693         
24694         var cfg = {
24695             tag : 'div',
24696             cls : 'btn-group',
24697             cn : [
24698                 {
24699                     tag : 'button',
24700                     cls : 'dropdown-button btn btn-' + this.weight,
24701                     cn : text
24702                 },
24703                 {
24704                     tag : 'button',
24705                     cls : 'dropdown-toggle btn btn-' + this.weight,
24706                     cn : [
24707                         {
24708                             tag : 'span',
24709                             cls : 'caret'
24710                         }
24711                     ]
24712                 },
24713                 {
24714                     tag : 'ul',
24715                     cls : 'dropdown-menu'
24716                 }
24717             ]
24718             
24719         };
24720         
24721         if(this.pos == 'top'){
24722             cfg.cls += ' dropup';
24723         }
24724         
24725         if(this.isSubMenu){
24726             cfg = {
24727                 tag : 'ul',
24728                 cls : 'dropdown-menu'
24729             }
24730         }
24731         
24732         return cfg;
24733     },
24734     
24735     onRender : function(ct, position)
24736     {
24737         this.isSubMenu = ct.hasClass('dropdown-submenu');
24738         
24739         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24740     },
24741     
24742     initEvents : function() 
24743     {
24744         if(this.isSubMenu){
24745             return;
24746         }
24747         
24748         this.hidden = true;
24749         
24750         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24751         this.triggerEl.on('click', this.onTriggerPress, this);
24752         
24753         this.buttonEl = this.el.select('button.dropdown-button', true).first();
24754         this.buttonEl.on('click', this.onClick, this);
24755         
24756     },
24757     
24758     list : function()
24759     {
24760         if(this.isSubMenu){
24761             return this.el;
24762         }
24763         
24764         return this.el.select('ul.dropdown-menu', true).first();
24765     },
24766     
24767     onClick : function(e)
24768     {
24769         this.fireEvent("click", this, e);
24770     },
24771     
24772     onTriggerPress  : function(e)
24773     {   
24774         if (this.isVisible()) {
24775             this.hide();
24776         } else {
24777             this.show();
24778         }
24779     },
24780     
24781     isVisible : function(){
24782         return !this.hidden;
24783     },
24784     
24785     show : function()
24786     {
24787         this.fireEvent("beforeshow", this);
24788         
24789         this.hidden = false;
24790         this.el.addClass('open');
24791         
24792         Roo.get(document).on("mouseup", this.onMouseUp, this);
24793         
24794         this.fireEvent("show", this);
24795         
24796         
24797     },
24798     
24799     hide : function()
24800     {
24801         this.fireEvent("beforehide", this);
24802         
24803         this.hidden = true;
24804         this.el.removeClass('open');
24805         
24806         Roo.get(document).un("mouseup", this.onMouseUp);
24807         
24808         this.fireEvent("hide", this);
24809     },
24810     
24811     onMouseUp : function()
24812     {
24813         this.hide();
24814     }
24815     
24816 });
24817
24818  
24819  /*
24820  * - LGPL
24821  *
24822  * menu item
24823  * 
24824  */
24825 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24826
24827 /**
24828  * @class Roo.bootstrap.menu.Item
24829  * @extends Roo.bootstrap.Component
24830  * Bootstrap MenuItem class
24831  * @cfg {Boolean} submenu (true | false) default false
24832  * @cfg {String} html text of the item
24833  * @cfg {String} href the link
24834  * @cfg {Boolean} disable (true | false) default false
24835  * @cfg {Boolean} preventDefault (true | false) default true
24836  * @cfg {String} icon Font awesome icon
24837  * @cfg {String} pos Submenu align to (left | right) default right 
24838  * 
24839  * 
24840  * @constructor
24841  * Create a new Item
24842  * @param {Object} config The config object
24843  */
24844
24845
24846 Roo.bootstrap.menu.Item = function(config){
24847     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24848     this.addEvents({
24849         /**
24850          * @event mouseover
24851          * Fires when the mouse is hovering over this menu
24852          * @param {Roo.bootstrap.menu.Item} this
24853          * @param {Roo.EventObject} e
24854          */
24855         mouseover : true,
24856         /**
24857          * @event mouseout
24858          * Fires when the mouse exits this menu
24859          * @param {Roo.bootstrap.menu.Item} this
24860          * @param {Roo.EventObject} e
24861          */
24862         mouseout : true,
24863         // raw events
24864         /**
24865          * @event click
24866          * The raw click event for the entire grid.
24867          * @param {Roo.EventObject} e
24868          */
24869         click : true
24870     });
24871 };
24872
24873 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
24874     
24875     submenu : false,
24876     href : '',
24877     html : '',
24878     preventDefault: true,
24879     disable : false,
24880     icon : false,
24881     pos : 'right',
24882     
24883     getAutoCreate : function()
24884     {
24885         var text = [
24886             {
24887                 tag : 'span',
24888                 cls : 'roo-menu-item-text',
24889                 html : this.html
24890             }
24891         ];
24892         
24893         if(this.icon){
24894             text.unshift({
24895                 tag : 'i',
24896                 cls : 'fa ' + this.icon
24897             })
24898         }
24899         
24900         var cfg = {
24901             tag : 'li',
24902             cn : [
24903                 {
24904                     tag : 'a',
24905                     href : this.href || '#',
24906                     cn : text
24907                 }
24908             ]
24909         };
24910         
24911         if(this.disable){
24912             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24913         }
24914         
24915         if(this.submenu){
24916             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24917             
24918             if(this.pos == 'left'){
24919                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24920             }
24921         }
24922         
24923         return cfg;
24924     },
24925     
24926     initEvents : function() 
24927     {
24928         this.el.on('mouseover', this.onMouseOver, this);
24929         this.el.on('mouseout', this.onMouseOut, this);
24930         
24931         this.el.select('a', true).first().on('click', this.onClick, this);
24932         
24933     },
24934     
24935     onClick : function(e)
24936     {
24937         if(this.preventDefault){
24938             e.preventDefault();
24939         }
24940         
24941         this.fireEvent("click", this, e);
24942     },
24943     
24944     onMouseOver : function(e)
24945     {
24946         if(this.submenu && this.pos == 'left'){
24947             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24948         }
24949         
24950         this.fireEvent("mouseover", this, e);
24951     },
24952     
24953     onMouseOut : function(e)
24954     {
24955         this.fireEvent("mouseout", this, e);
24956     }
24957 });
24958
24959  
24960
24961  /*
24962  * - LGPL
24963  *
24964  * menu separator
24965  * 
24966  */
24967 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24968
24969 /**
24970  * @class Roo.bootstrap.menu.Separator
24971  * @extends Roo.bootstrap.Component
24972  * Bootstrap Separator class
24973  * 
24974  * @constructor
24975  * Create a new Separator
24976  * @param {Object} config The config object
24977  */
24978
24979
24980 Roo.bootstrap.menu.Separator = function(config){
24981     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24982 };
24983
24984 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
24985     
24986     getAutoCreate : function(){
24987         var cfg = {
24988             tag : 'li',
24989             cls: 'divider'
24990         };
24991         
24992         return cfg;
24993     }
24994    
24995 });
24996
24997  
24998
24999  /*
25000  * - LGPL
25001  *
25002  * Tooltip
25003  * 
25004  */
25005
25006 /**
25007  * @class Roo.bootstrap.Tooltip
25008  * Bootstrap Tooltip class
25009  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25010  * to determine which dom element triggers the tooltip.
25011  * 
25012  * It needs to add support for additional attributes like tooltip-position
25013  * 
25014  * @constructor
25015  * Create a new Toolti
25016  * @param {Object} config The config object
25017  */
25018
25019 Roo.bootstrap.Tooltip = function(config){
25020     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25021     
25022     this.alignment = Roo.bootstrap.Tooltip.alignment;
25023     
25024     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25025         this.alignment = config.alignment;
25026     }
25027     
25028 };
25029
25030 Roo.apply(Roo.bootstrap.Tooltip, {
25031     /**
25032      * @function init initialize tooltip monitoring.
25033      * @static
25034      */
25035     currentEl : false,
25036     currentTip : false,
25037     currentRegion : false,
25038     
25039     //  init : delay?
25040     
25041     init : function()
25042     {
25043         Roo.get(document).on('mouseover', this.enter ,this);
25044         Roo.get(document).on('mouseout', this.leave, this);
25045          
25046         
25047         this.currentTip = new Roo.bootstrap.Tooltip();
25048     },
25049     
25050     enter : function(ev)
25051     {
25052         var dom = ev.getTarget();
25053         
25054         //Roo.log(['enter',dom]);
25055         var el = Roo.fly(dom);
25056         if (this.currentEl) {
25057             //Roo.log(dom);
25058             //Roo.log(this.currentEl);
25059             //Roo.log(this.currentEl.contains(dom));
25060             if (this.currentEl == el) {
25061                 return;
25062             }
25063             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25064                 return;
25065             }
25066
25067         }
25068         
25069         if (this.currentTip.el) {
25070             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25071         }    
25072         //Roo.log(ev);
25073         
25074         if(!el || el.dom == document){
25075             return;
25076         }
25077         
25078         var bindEl = el;
25079         
25080         // you can not look for children, as if el is the body.. then everythign is the child..
25081         if (!el.attr('tooltip')) { //
25082             if (!el.select("[tooltip]").elements.length) {
25083                 return;
25084             }
25085             // is the mouse over this child...?
25086             bindEl = el.select("[tooltip]").first();
25087             var xy = ev.getXY();
25088             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25089                 //Roo.log("not in region.");
25090                 return;
25091             }
25092             //Roo.log("child element over..");
25093             
25094         }
25095         this.currentEl = bindEl;
25096         this.currentTip.bind(bindEl);
25097         this.currentRegion = Roo.lib.Region.getRegion(dom);
25098         this.currentTip.enter();
25099         
25100     },
25101     leave : function(ev)
25102     {
25103         var dom = ev.getTarget();
25104         //Roo.log(['leave',dom]);
25105         if (!this.currentEl) {
25106             return;
25107         }
25108         
25109         
25110         if (dom != this.currentEl.dom) {
25111             return;
25112         }
25113         var xy = ev.getXY();
25114         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25115             return;
25116         }
25117         // only activate leave if mouse cursor is outside... bounding box..
25118         
25119         
25120         
25121         
25122         if (this.currentTip) {
25123             this.currentTip.leave();
25124         }
25125         //Roo.log('clear currentEl');
25126         this.currentEl = false;
25127         
25128         
25129     },
25130     alignment : {
25131         'left' : ['r-l', [-2,0], 'right'],
25132         'right' : ['l-r', [2,0], 'left'],
25133         'bottom' : ['t-b', [0,2], 'top'],
25134         'top' : [ 'b-t', [0,-2], 'bottom']
25135     }
25136     
25137 });
25138
25139
25140 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25141     
25142     
25143     bindEl : false,
25144     
25145     delay : null, // can be { show : 300 , hide: 500}
25146     
25147     timeout : null,
25148     
25149     hoverState : null, //???
25150     
25151     placement : 'bottom', 
25152     
25153     alignment : false,
25154     
25155     getAutoCreate : function(){
25156     
25157         var cfg = {
25158            cls : 'tooltip',
25159            role : 'tooltip',
25160            cn : [
25161                 {
25162                     cls : 'tooltip-arrow'
25163                 },
25164                 {
25165                     cls : 'tooltip-inner'
25166                 }
25167            ]
25168         };
25169         
25170         return cfg;
25171     },
25172     bind : function(el)
25173     {
25174         this.bindEl = el;
25175     },
25176       
25177     
25178     enter : function () {
25179        
25180         if (this.timeout != null) {
25181             clearTimeout(this.timeout);
25182         }
25183         
25184         this.hoverState = 'in';
25185          //Roo.log("enter - show");
25186         if (!this.delay || !this.delay.show) {
25187             this.show();
25188             return;
25189         }
25190         var _t = this;
25191         this.timeout = setTimeout(function () {
25192             if (_t.hoverState == 'in') {
25193                 _t.show();
25194             }
25195         }, this.delay.show);
25196     },
25197     leave : function()
25198     {
25199         clearTimeout(this.timeout);
25200     
25201         this.hoverState = 'out';
25202          if (!this.delay || !this.delay.hide) {
25203             this.hide();
25204             return;
25205         }
25206        
25207         var _t = this;
25208         this.timeout = setTimeout(function () {
25209             //Roo.log("leave - timeout");
25210             
25211             if (_t.hoverState == 'out') {
25212                 _t.hide();
25213                 Roo.bootstrap.Tooltip.currentEl = false;
25214             }
25215         }, delay);
25216     },
25217     
25218     show : function (msg)
25219     {
25220         if (!this.el) {
25221             this.render(document.body);
25222         }
25223         // set content.
25224         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25225         
25226         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25227         
25228         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25229         
25230         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25231         
25232         var placement = typeof this.placement == 'function' ?
25233             this.placement.call(this, this.el, on_el) :
25234             this.placement;
25235             
25236         var autoToken = /\s?auto?\s?/i;
25237         var autoPlace = autoToken.test(placement);
25238         if (autoPlace) {
25239             placement = placement.replace(autoToken, '') || 'top';
25240         }
25241         
25242         //this.el.detach()
25243         //this.el.setXY([0,0]);
25244         this.el.show();
25245         //this.el.dom.style.display='block';
25246         
25247         //this.el.appendTo(on_el);
25248         
25249         var p = this.getPosition();
25250         var box = this.el.getBox();
25251         
25252         if (autoPlace) {
25253             // fixme..
25254         }
25255         
25256         var align = this.alignment[placement];
25257         
25258         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25259         
25260         if(placement == 'top' || placement == 'bottom'){
25261             if(xy[0] < 0){
25262                 placement = 'right';
25263             }
25264             
25265             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25266                 placement = 'left';
25267             }
25268             
25269             var scroll = Roo.select('body', true).first().getScroll();
25270             
25271             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25272                 placement = 'top';
25273             }
25274             
25275         }
25276         
25277         this.el.alignTo(this.bindEl, align[0],align[1]);
25278         //var arrow = this.el.select('.arrow',true).first();
25279         //arrow.set(align[2], 
25280         
25281         this.el.addClass(placement);
25282         
25283         this.el.addClass('in fade');
25284         
25285         this.hoverState = null;
25286         
25287         if (this.el.hasClass('fade')) {
25288             // fade it?
25289         }
25290         
25291     },
25292     hide : function()
25293     {
25294          
25295         if (!this.el) {
25296             return;
25297         }
25298         //this.el.setXY([0,0]);
25299         this.el.removeClass('in');
25300         //this.el.hide();
25301         
25302     }
25303     
25304 });
25305  
25306
25307  /*
25308  * - LGPL
25309  *
25310  * Location Picker
25311  * 
25312  */
25313
25314 /**
25315  * @class Roo.bootstrap.LocationPicker
25316  * @extends Roo.bootstrap.Component
25317  * Bootstrap LocationPicker class
25318  * @cfg {Number} latitude Position when init default 0
25319  * @cfg {Number} longitude Position when init default 0
25320  * @cfg {Number} zoom default 15
25321  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25322  * @cfg {Boolean} mapTypeControl default false
25323  * @cfg {Boolean} disableDoubleClickZoom default false
25324  * @cfg {Boolean} scrollwheel default true
25325  * @cfg {Boolean} streetViewControl default false
25326  * @cfg {Number} radius default 0
25327  * @cfg {String} locationName
25328  * @cfg {Boolean} draggable default true
25329  * @cfg {Boolean} enableAutocomplete default false
25330  * @cfg {Boolean} enableReverseGeocode default true
25331  * @cfg {String} markerTitle
25332  * 
25333  * @constructor
25334  * Create a new LocationPicker
25335  * @param {Object} config The config object
25336  */
25337
25338
25339 Roo.bootstrap.LocationPicker = function(config){
25340     
25341     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25342     
25343     this.addEvents({
25344         /**
25345          * @event initial
25346          * Fires when the picker initialized.
25347          * @param {Roo.bootstrap.LocationPicker} this
25348          * @param {Google Location} location
25349          */
25350         initial : true,
25351         /**
25352          * @event positionchanged
25353          * Fires when the picker position changed.
25354          * @param {Roo.bootstrap.LocationPicker} this
25355          * @param {Google Location} location
25356          */
25357         positionchanged : true,
25358         /**
25359          * @event resize
25360          * Fires when the map resize.
25361          * @param {Roo.bootstrap.LocationPicker} this
25362          */
25363         resize : true,
25364         /**
25365          * @event show
25366          * Fires when the map show.
25367          * @param {Roo.bootstrap.LocationPicker} this
25368          */
25369         show : true,
25370         /**
25371          * @event hide
25372          * Fires when the map hide.
25373          * @param {Roo.bootstrap.LocationPicker} this
25374          */
25375         hide : true,
25376         /**
25377          * @event mapClick
25378          * Fires when click the map.
25379          * @param {Roo.bootstrap.LocationPicker} this
25380          * @param {Map event} e
25381          */
25382         mapClick : true,
25383         /**
25384          * @event mapRightClick
25385          * Fires when right click the map.
25386          * @param {Roo.bootstrap.LocationPicker} this
25387          * @param {Map event} e
25388          */
25389         mapRightClick : true,
25390         /**
25391          * @event markerClick
25392          * Fires when click the marker.
25393          * @param {Roo.bootstrap.LocationPicker} this
25394          * @param {Map event} e
25395          */
25396         markerClick : true,
25397         /**
25398          * @event markerRightClick
25399          * Fires when right click the marker.
25400          * @param {Roo.bootstrap.LocationPicker} this
25401          * @param {Map event} e
25402          */
25403         markerRightClick : true,
25404         /**
25405          * @event OverlayViewDraw
25406          * Fires when OverlayView Draw
25407          * @param {Roo.bootstrap.LocationPicker} this
25408          */
25409         OverlayViewDraw : true,
25410         /**
25411          * @event OverlayViewOnAdd
25412          * Fires when OverlayView Draw
25413          * @param {Roo.bootstrap.LocationPicker} this
25414          */
25415         OverlayViewOnAdd : true,
25416         /**
25417          * @event OverlayViewOnRemove
25418          * Fires when OverlayView Draw
25419          * @param {Roo.bootstrap.LocationPicker} this
25420          */
25421         OverlayViewOnRemove : true,
25422         /**
25423          * @event OverlayViewShow
25424          * Fires when OverlayView Draw
25425          * @param {Roo.bootstrap.LocationPicker} this
25426          * @param {Pixel} cpx
25427          */
25428         OverlayViewShow : true,
25429         /**
25430          * @event OverlayViewHide
25431          * Fires when OverlayView Draw
25432          * @param {Roo.bootstrap.LocationPicker} this
25433          */
25434         OverlayViewHide : true,
25435         /**
25436          * @event loadexception
25437          * Fires when load google lib failed.
25438          * @param {Roo.bootstrap.LocationPicker} this
25439          */
25440         loadexception : true
25441     });
25442         
25443 };
25444
25445 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
25446     
25447     gMapContext: false,
25448     
25449     latitude: 0,
25450     longitude: 0,
25451     zoom: 15,
25452     mapTypeId: false,
25453     mapTypeControl: false,
25454     disableDoubleClickZoom: false,
25455     scrollwheel: true,
25456     streetViewControl: false,
25457     radius: 0,
25458     locationName: '',
25459     draggable: true,
25460     enableAutocomplete: false,
25461     enableReverseGeocode: true,
25462     markerTitle: '',
25463     
25464     getAutoCreate: function()
25465     {
25466
25467         var cfg = {
25468             tag: 'div',
25469             cls: 'roo-location-picker'
25470         };
25471         
25472         return cfg
25473     },
25474     
25475     initEvents: function(ct, position)
25476     {       
25477         if(!this.el.getWidth() || this.isApplied()){
25478             return;
25479         }
25480         
25481         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25482         
25483         this.initial();
25484     },
25485     
25486     initial: function()
25487     {
25488         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
25489             this.fireEvent('loadexception', this);
25490             return;
25491         }
25492         
25493         if(!this.mapTypeId){
25494             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
25495         }
25496         
25497         this.gMapContext = this.GMapContext();
25498         
25499         this.initOverlayView();
25500         
25501         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
25502         
25503         var _this = this;
25504                 
25505         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
25506             _this.setPosition(_this.gMapContext.marker.position);
25507         });
25508         
25509         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
25510             _this.fireEvent('mapClick', this, event);
25511             
25512         });
25513
25514         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
25515             _this.fireEvent('mapRightClick', this, event);
25516             
25517         });
25518         
25519         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
25520             _this.fireEvent('markerClick', this, event);
25521             
25522         });
25523
25524         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
25525             _this.fireEvent('markerRightClick', this, event);
25526             
25527         });
25528         
25529         this.setPosition(this.gMapContext.location);
25530         
25531         this.fireEvent('initial', this, this.gMapContext.location);
25532     },
25533     
25534     initOverlayView: function()
25535     {
25536         var _this = this;
25537         
25538         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
25539             
25540             draw: function()
25541             {
25542                 _this.fireEvent('OverlayViewDraw', _this);
25543             },
25544             
25545             onAdd: function()
25546             {
25547                 _this.fireEvent('OverlayViewOnAdd', _this);
25548             },
25549             
25550             onRemove: function()
25551             {
25552                 _this.fireEvent('OverlayViewOnRemove', _this);
25553             },
25554             
25555             show: function(cpx)
25556             {
25557                 _this.fireEvent('OverlayViewShow', _this, cpx);
25558             },
25559             
25560             hide: function()
25561             {
25562                 _this.fireEvent('OverlayViewHide', _this);
25563             }
25564             
25565         });
25566     },
25567     
25568     fromLatLngToContainerPixel: function(event)
25569     {
25570         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
25571     },
25572     
25573     isApplied: function() 
25574     {
25575         return this.getGmapContext() == false ? false : true;
25576     },
25577     
25578     getGmapContext: function() 
25579     {
25580         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
25581     },
25582     
25583     GMapContext: function() 
25584     {
25585         var position = new google.maps.LatLng(this.latitude, this.longitude);
25586         
25587         var _map = new google.maps.Map(this.el.dom, {
25588             center: position,
25589             zoom: this.zoom,
25590             mapTypeId: this.mapTypeId,
25591             mapTypeControl: this.mapTypeControl,
25592             disableDoubleClickZoom: this.disableDoubleClickZoom,
25593             scrollwheel: this.scrollwheel,
25594             streetViewControl: this.streetViewControl,
25595             locationName: this.locationName,
25596             draggable: this.draggable,
25597             enableAutocomplete: this.enableAutocomplete,
25598             enableReverseGeocode: this.enableReverseGeocode
25599         });
25600         
25601         var _marker = new google.maps.Marker({
25602             position: position,
25603             map: _map,
25604             title: this.markerTitle,
25605             draggable: this.draggable
25606         });
25607         
25608         return {
25609             map: _map,
25610             marker: _marker,
25611             circle: null,
25612             location: position,
25613             radius: this.radius,
25614             locationName: this.locationName,
25615             addressComponents: {
25616                 formatted_address: null,
25617                 addressLine1: null,
25618                 addressLine2: null,
25619                 streetName: null,
25620                 streetNumber: null,
25621                 city: null,
25622                 district: null,
25623                 state: null,
25624                 stateOrProvince: null
25625             },
25626             settings: this,
25627             domContainer: this.el.dom,
25628             geodecoder: new google.maps.Geocoder()
25629         };
25630     },
25631     
25632     drawCircle: function(center, radius, options) 
25633     {
25634         if (this.gMapContext.circle != null) {
25635             this.gMapContext.circle.setMap(null);
25636         }
25637         if (radius > 0) {
25638             radius *= 1;
25639             options = Roo.apply({}, options, {
25640                 strokeColor: "#0000FF",
25641                 strokeOpacity: .35,
25642                 strokeWeight: 2,
25643                 fillColor: "#0000FF",
25644                 fillOpacity: .2
25645             });
25646             
25647             options.map = this.gMapContext.map;
25648             options.radius = radius;
25649             options.center = center;
25650             this.gMapContext.circle = new google.maps.Circle(options);
25651             return this.gMapContext.circle;
25652         }
25653         
25654         return null;
25655     },
25656     
25657     setPosition: function(location) 
25658     {
25659         this.gMapContext.location = location;
25660         this.gMapContext.marker.setPosition(location);
25661         this.gMapContext.map.panTo(location);
25662         this.drawCircle(location, this.gMapContext.radius, {});
25663         
25664         var _this = this;
25665         
25666         if (this.gMapContext.settings.enableReverseGeocode) {
25667             this.gMapContext.geodecoder.geocode({
25668                 latLng: this.gMapContext.location
25669             }, function(results, status) {
25670                 
25671                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25672                     _this.gMapContext.locationName = results[0].formatted_address;
25673                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25674                     
25675                     _this.fireEvent('positionchanged', this, location);
25676                 }
25677             });
25678             
25679             return;
25680         }
25681         
25682         this.fireEvent('positionchanged', this, location);
25683     },
25684     
25685     resize: function()
25686     {
25687         google.maps.event.trigger(this.gMapContext.map, "resize");
25688         
25689         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25690         
25691         this.fireEvent('resize', this);
25692     },
25693     
25694     setPositionByLatLng: function(latitude, longitude)
25695     {
25696         this.setPosition(new google.maps.LatLng(latitude, longitude));
25697     },
25698     
25699     getCurrentPosition: function() 
25700     {
25701         return {
25702             latitude: this.gMapContext.location.lat(),
25703             longitude: this.gMapContext.location.lng()
25704         };
25705     },
25706     
25707     getAddressName: function() 
25708     {
25709         return this.gMapContext.locationName;
25710     },
25711     
25712     getAddressComponents: function() 
25713     {
25714         return this.gMapContext.addressComponents;
25715     },
25716     
25717     address_component_from_google_geocode: function(address_components) 
25718     {
25719         var result = {};
25720         
25721         for (var i = 0; i < address_components.length; i++) {
25722             var component = address_components[i];
25723             if (component.types.indexOf("postal_code") >= 0) {
25724                 result.postalCode = component.short_name;
25725             } else if (component.types.indexOf("street_number") >= 0) {
25726                 result.streetNumber = component.short_name;
25727             } else if (component.types.indexOf("route") >= 0) {
25728                 result.streetName = component.short_name;
25729             } else if (component.types.indexOf("neighborhood") >= 0) {
25730                 result.city = component.short_name;
25731             } else if (component.types.indexOf("locality") >= 0) {
25732                 result.city = component.short_name;
25733             } else if (component.types.indexOf("sublocality") >= 0) {
25734                 result.district = component.short_name;
25735             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25736                 result.stateOrProvince = component.short_name;
25737             } else if (component.types.indexOf("country") >= 0) {
25738                 result.country = component.short_name;
25739             }
25740         }
25741         
25742         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25743         result.addressLine2 = "";
25744         return result;
25745     },
25746     
25747     setZoomLevel: function(zoom)
25748     {
25749         this.gMapContext.map.setZoom(zoom);
25750     },
25751     
25752     show: function()
25753     {
25754         if(!this.el){
25755             return;
25756         }
25757         
25758         this.el.show();
25759         
25760         this.resize();
25761         
25762         this.fireEvent('show', this);
25763     },
25764     
25765     hide: function()
25766     {
25767         if(!this.el){
25768             return;
25769         }
25770         
25771         this.el.hide();
25772         
25773         this.fireEvent('hide', this);
25774     }
25775     
25776 });
25777
25778 Roo.apply(Roo.bootstrap.LocationPicker, {
25779     
25780     OverlayView : function(map, options)
25781     {
25782         options = options || {};
25783         
25784         this.setMap(map);
25785     }
25786     
25787     
25788 });/*
25789  * - LGPL
25790  *
25791  * Alert
25792  * 
25793  */
25794
25795 /**
25796  * @class Roo.bootstrap.Alert
25797  * @extends Roo.bootstrap.Component
25798  * Bootstrap Alert class
25799  * @cfg {String} title The title of alert
25800  * @cfg {String} html The content of alert
25801  * @cfg {String} weight (  success | info | warning | danger )
25802  * @cfg {String} faicon font-awesomeicon
25803  * 
25804  * @constructor
25805  * Create a new alert
25806  * @param {Object} config The config object
25807  */
25808
25809
25810 Roo.bootstrap.Alert = function(config){
25811     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25812     
25813 };
25814
25815 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
25816     
25817     title: '',
25818     html: '',
25819     weight: false,
25820     faicon: false,
25821     
25822     getAutoCreate : function()
25823     {
25824         
25825         var cfg = {
25826             tag : 'div',
25827             cls : 'alert',
25828             cn : [
25829                 {
25830                     tag : 'i',
25831                     cls : 'roo-alert-icon'
25832                     
25833                 },
25834                 {
25835                     tag : 'b',
25836                     cls : 'roo-alert-title',
25837                     html : this.title
25838                 },
25839                 {
25840                     tag : 'span',
25841                     cls : 'roo-alert-text',
25842                     html : this.html
25843                 }
25844             ]
25845         };
25846         
25847         if(this.faicon){
25848             cfg.cn[0].cls += ' fa ' + this.faicon;
25849         }
25850         
25851         if(this.weight){
25852             cfg.cls += ' alert-' + this.weight;
25853         }
25854         
25855         return cfg;
25856     },
25857     
25858     initEvents: function() 
25859     {
25860         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25861     },
25862     
25863     setTitle : function(str)
25864     {
25865         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25866     },
25867     
25868     setText : function(str)
25869     {
25870         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25871     },
25872     
25873     setWeight : function(weight)
25874     {
25875         if(this.weight){
25876             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25877         }
25878         
25879         this.weight = weight;
25880         
25881         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25882     },
25883     
25884     setIcon : function(icon)
25885     {
25886         if(this.faicon){
25887             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25888         }
25889         
25890         this.faicon = icon;
25891         
25892         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25893     },
25894     
25895     hide: function() 
25896     {
25897         this.el.hide();   
25898     },
25899     
25900     show: function() 
25901     {  
25902         this.el.show();   
25903     }
25904     
25905 });
25906
25907  
25908 /*
25909 * Licence: LGPL
25910 */
25911
25912 /**
25913  * @class Roo.bootstrap.UploadCropbox
25914  * @extends Roo.bootstrap.Component
25915  * Bootstrap UploadCropbox class
25916  * @cfg {String} emptyText show when image has been loaded
25917  * @cfg {String} rotateNotify show when image too small to rotate
25918  * @cfg {Number} errorTimeout default 3000
25919  * @cfg {Number} minWidth default 300
25920  * @cfg {Number} minHeight default 300
25921  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25922  * @cfg {Boolean} isDocument (true|false) default false
25923  * @cfg {String} url action url
25924  * @cfg {String} paramName default 'imageUpload'
25925  * @cfg {String} method default POST
25926  * @cfg {Boolean} loadMask (true|false) default true
25927  * @cfg {Boolean} loadingText default 'Loading...'
25928  * 
25929  * @constructor
25930  * Create a new UploadCropbox
25931  * @param {Object} config The config object
25932  */
25933
25934 Roo.bootstrap.UploadCropbox = function(config){
25935     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25936     
25937     this.addEvents({
25938         /**
25939          * @event beforeselectfile
25940          * Fire before select file
25941          * @param {Roo.bootstrap.UploadCropbox} this
25942          */
25943         "beforeselectfile" : true,
25944         /**
25945          * @event initial
25946          * Fire after initEvent
25947          * @param {Roo.bootstrap.UploadCropbox} this
25948          */
25949         "initial" : true,
25950         /**
25951          * @event crop
25952          * Fire after initEvent
25953          * @param {Roo.bootstrap.UploadCropbox} this
25954          * @param {String} data
25955          */
25956         "crop" : true,
25957         /**
25958          * @event prepare
25959          * Fire when preparing the file data
25960          * @param {Roo.bootstrap.UploadCropbox} this
25961          * @param {Object} file
25962          */
25963         "prepare" : true,
25964         /**
25965          * @event exception
25966          * Fire when get exception
25967          * @param {Roo.bootstrap.UploadCropbox} this
25968          * @param {XMLHttpRequest} xhr
25969          */
25970         "exception" : true,
25971         /**
25972          * @event beforeloadcanvas
25973          * Fire before load the canvas
25974          * @param {Roo.bootstrap.UploadCropbox} this
25975          * @param {String} src
25976          */
25977         "beforeloadcanvas" : true,
25978         /**
25979          * @event trash
25980          * Fire when trash image
25981          * @param {Roo.bootstrap.UploadCropbox} this
25982          */
25983         "trash" : true,
25984         /**
25985          * @event download
25986          * Fire when download the image
25987          * @param {Roo.bootstrap.UploadCropbox} this
25988          */
25989         "download" : true,
25990         /**
25991          * @event footerbuttonclick
25992          * Fire when footerbuttonclick
25993          * @param {Roo.bootstrap.UploadCropbox} this
25994          * @param {String} type
25995          */
25996         "footerbuttonclick" : true,
25997         /**
25998          * @event resize
25999          * Fire when resize
26000          * @param {Roo.bootstrap.UploadCropbox} this
26001          */
26002         "resize" : true,
26003         /**
26004          * @event rotate
26005          * Fire when rotate the image
26006          * @param {Roo.bootstrap.UploadCropbox} this
26007          * @param {String} pos
26008          */
26009         "rotate" : true,
26010         /**
26011          * @event inspect
26012          * Fire when inspect the file
26013          * @param {Roo.bootstrap.UploadCropbox} this
26014          * @param {Object} file
26015          */
26016         "inspect" : true,
26017         /**
26018          * @event upload
26019          * Fire when xhr upload the file
26020          * @param {Roo.bootstrap.UploadCropbox} this
26021          * @param {Object} data
26022          */
26023         "upload" : true,
26024         /**
26025          * @event arrange
26026          * Fire when arrange the file data
26027          * @param {Roo.bootstrap.UploadCropbox} this
26028          * @param {Object} formData
26029          */
26030         "arrange" : true
26031     });
26032     
26033     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26034 };
26035
26036 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26037     
26038     emptyText : 'Click to upload image',
26039     rotateNotify : 'Image is too small to rotate',
26040     errorTimeout : 3000,
26041     scale : 0,
26042     baseScale : 1,
26043     rotate : 0,
26044     dragable : false,
26045     pinching : false,
26046     mouseX : 0,
26047     mouseY : 0,
26048     cropData : false,
26049     minWidth : 300,
26050     minHeight : 300,
26051     file : false,
26052     exif : {},
26053     baseRotate : 1,
26054     cropType : 'image/jpeg',
26055     buttons : false,
26056     canvasLoaded : false,
26057     isDocument : false,
26058     method : 'POST',
26059     paramName : 'imageUpload',
26060     loadMask : true,
26061     loadingText : 'Loading...',
26062     maskEl : false,
26063     
26064     getAutoCreate : function()
26065     {
26066         var cfg = {
26067             tag : 'div',
26068             cls : 'roo-upload-cropbox',
26069             cn : [
26070                 {
26071                     tag : 'input',
26072                     cls : 'roo-upload-cropbox-selector',
26073                     type : 'file'
26074                 },
26075                 {
26076                     tag : 'div',
26077                     cls : 'roo-upload-cropbox-body',
26078                     style : 'cursor:pointer',
26079                     cn : [
26080                         {
26081                             tag : 'div',
26082                             cls : 'roo-upload-cropbox-preview'
26083                         },
26084                         {
26085                             tag : 'div',
26086                             cls : 'roo-upload-cropbox-thumb'
26087                         },
26088                         {
26089                             tag : 'div',
26090                             cls : 'roo-upload-cropbox-empty-notify',
26091                             html : this.emptyText
26092                         },
26093                         {
26094                             tag : 'div',
26095                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26096                             html : this.rotateNotify
26097                         }
26098                     ]
26099                 },
26100                 {
26101                     tag : 'div',
26102                     cls : 'roo-upload-cropbox-footer',
26103                     cn : {
26104                         tag : 'div',
26105                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26106                         cn : []
26107                     }
26108                 }
26109             ]
26110         };
26111         
26112         return cfg;
26113     },
26114     
26115     onRender : function(ct, position)
26116     {
26117         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26118         
26119         if (this.buttons.length) {
26120             
26121             Roo.each(this.buttons, function(bb) {
26122                 
26123                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26124                 
26125                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26126                 
26127             }, this);
26128         }
26129         
26130         if(this.loadMask){
26131             this.maskEl = this.el;
26132         }
26133     },
26134     
26135     initEvents : function()
26136     {
26137         this.urlAPI = (window.createObjectURL && window) || 
26138                                 (window.URL && URL.revokeObjectURL && URL) || 
26139                                 (window.webkitURL && webkitURL);
26140                         
26141         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26142         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26143         
26144         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26145         this.selectorEl.hide();
26146         
26147         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26148         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26149         
26150         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26151         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26152         this.thumbEl.hide();
26153         
26154         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26155         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26156         
26157         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26158         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26159         this.errorEl.hide();
26160         
26161         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26162         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26163         this.footerEl.hide();
26164         
26165         this.setThumbBoxSize();
26166         
26167         this.bind();
26168         
26169         this.resize();
26170         
26171         this.fireEvent('initial', this);
26172     },
26173
26174     bind : function()
26175     {
26176         var _this = this;
26177         
26178         window.addEventListener("resize", function() { _this.resize(); } );
26179         
26180         this.bodyEl.on('click', this.beforeSelectFile, this);
26181         
26182         if(Roo.isTouch){
26183             this.bodyEl.on('touchstart', this.onTouchStart, this);
26184             this.bodyEl.on('touchmove', this.onTouchMove, this);
26185             this.bodyEl.on('touchend', this.onTouchEnd, this);
26186         }
26187         
26188         if(!Roo.isTouch){
26189             this.bodyEl.on('mousedown', this.onMouseDown, this);
26190             this.bodyEl.on('mousemove', this.onMouseMove, this);
26191             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26192             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26193             Roo.get(document).on('mouseup', this.onMouseUp, this);
26194         }
26195         
26196         this.selectorEl.on('change', this.onFileSelected, this);
26197     },
26198     
26199     reset : function()
26200     {    
26201         this.scale = 0;
26202         this.baseScale = 1;
26203         this.rotate = 0;
26204         this.baseRotate = 1;
26205         this.dragable = false;
26206         this.pinching = false;
26207         this.mouseX = 0;
26208         this.mouseY = 0;
26209         this.cropData = false;
26210         this.notifyEl.dom.innerHTML = this.emptyText;
26211         
26212         this.selectorEl.dom.value = '';
26213         
26214     },
26215     
26216     resize : function()
26217     {
26218         if(this.fireEvent('resize', this) != false){
26219             this.setThumbBoxPosition();
26220             this.setCanvasPosition();
26221         }
26222     },
26223     
26224     onFooterButtonClick : function(e, el, o, type)
26225     {
26226         switch (type) {
26227             case 'rotate-left' :
26228                 this.onRotateLeft(e);
26229                 break;
26230             case 'rotate-right' :
26231                 this.onRotateRight(e);
26232                 break;
26233             case 'picture' :
26234                 this.beforeSelectFile(e);
26235                 break;
26236             case 'trash' :
26237                 this.trash(e);
26238                 break;
26239             case 'crop' :
26240                 this.crop(e);
26241                 break;
26242             case 'download' :
26243                 this.download(e);
26244                 break;
26245             default :
26246                 break;
26247         }
26248         
26249         this.fireEvent('footerbuttonclick', this, type);
26250     },
26251     
26252     beforeSelectFile : function(e)
26253     {
26254         e.preventDefault();
26255         
26256         if(this.fireEvent('beforeselectfile', this) != false){
26257             this.selectorEl.dom.click();
26258         }
26259     },
26260     
26261     onFileSelected : function(e)
26262     {
26263         e.preventDefault();
26264         
26265         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26266             return;
26267         }
26268         
26269         var file = this.selectorEl.dom.files[0];
26270         
26271         if(this.fireEvent('inspect', this, file) != false){
26272             this.prepare(file);
26273         }
26274         
26275     },
26276     
26277     trash : function(e)
26278     {
26279         this.fireEvent('trash', this);
26280     },
26281     
26282     download : function(e)
26283     {
26284         this.fireEvent('download', this);
26285     },
26286     
26287     loadCanvas : function(src)
26288     {   
26289         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26290             
26291             this.reset();
26292             
26293             this.imageEl = document.createElement('img');
26294             
26295             var _this = this;
26296             
26297             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26298             
26299             this.imageEl.src = src;
26300         }
26301     },
26302     
26303     onLoadCanvas : function()
26304     {   
26305         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26306         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26307         
26308         this.bodyEl.un('click', this.beforeSelectFile, this);
26309         
26310         this.notifyEl.hide();
26311         this.thumbEl.show();
26312         this.footerEl.show();
26313         
26314         this.baseRotateLevel();
26315         
26316         if(this.isDocument){
26317             this.setThumbBoxSize();
26318         }
26319         
26320         this.setThumbBoxPosition();
26321         
26322         this.baseScaleLevel();
26323         
26324         this.draw();
26325         
26326         this.resize();
26327         
26328         this.canvasLoaded = true;
26329         
26330         if(this.loadMask){
26331             this.maskEl.unmask();
26332         }
26333         
26334     },
26335     
26336     setCanvasPosition : function()
26337     {   
26338         if(!this.canvasEl){
26339             return;
26340         }
26341         
26342         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26343         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26344         
26345         this.previewEl.setLeft(pw);
26346         this.previewEl.setTop(ph);
26347         
26348     },
26349     
26350     onMouseDown : function(e)
26351     {   
26352         e.stopEvent();
26353         
26354         this.dragable = true;
26355         this.pinching = false;
26356         
26357         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26358             this.dragable = false;
26359             return;
26360         }
26361         
26362         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26363         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26364         
26365     },
26366     
26367     onMouseMove : function(e)
26368     {   
26369         e.stopEvent();
26370         
26371         if(!this.canvasLoaded){
26372             return;
26373         }
26374         
26375         if (!this.dragable){
26376             return;
26377         }
26378         
26379         var minX = Math.ceil(this.thumbEl.getLeft(true));
26380         var minY = Math.ceil(this.thumbEl.getTop(true));
26381         
26382         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26383         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26384         
26385         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26386         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26387         
26388         x = x - this.mouseX;
26389         y = y - this.mouseY;
26390         
26391         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26392         var bgY = Math.ceil(y + this.previewEl.getTop(true));
26393         
26394         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26395         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26396         
26397         this.previewEl.setLeft(bgX);
26398         this.previewEl.setTop(bgY);
26399         
26400         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26401         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26402     },
26403     
26404     onMouseUp : function(e)
26405     {   
26406         e.stopEvent();
26407         
26408         this.dragable = false;
26409     },
26410     
26411     onMouseWheel : function(e)
26412     {   
26413         e.stopEvent();
26414         
26415         this.startScale = this.scale;
26416         
26417         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26418         
26419         if(!this.zoomable()){
26420             this.scale = this.startScale;
26421             return;
26422         }
26423         
26424         this.draw();
26425         
26426         return;
26427     },
26428     
26429     zoomable : function()
26430     {
26431         var minScale = this.thumbEl.getWidth() / this.minWidth;
26432         
26433         if(this.minWidth < this.minHeight){
26434             minScale = this.thumbEl.getHeight() / this.minHeight;
26435         }
26436         
26437         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26438         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26439         
26440         if(
26441                 this.isDocument &&
26442                 (this.rotate == 0 || this.rotate == 180) && 
26443                 (
26444                     width > this.imageEl.OriginWidth || 
26445                     height > this.imageEl.OriginHeight ||
26446                     (width < this.minWidth && height < this.minHeight)
26447                 )
26448         ){
26449             return false;
26450         }
26451         
26452         if(
26453                 this.isDocument &&
26454                 (this.rotate == 90 || this.rotate == 270) && 
26455                 (
26456                     width > this.imageEl.OriginWidth || 
26457                     height > this.imageEl.OriginHeight ||
26458                     (width < this.minHeight && height < this.minWidth)
26459                 )
26460         ){
26461             return false;
26462         }
26463         
26464         if(
26465                 !this.isDocument &&
26466                 (this.rotate == 0 || this.rotate == 180) && 
26467                 (
26468                     width < this.minWidth || 
26469                     width > this.imageEl.OriginWidth || 
26470                     height < this.minHeight || 
26471                     height > this.imageEl.OriginHeight
26472                 )
26473         ){
26474             return false;
26475         }
26476         
26477         if(
26478                 !this.isDocument &&
26479                 (this.rotate == 90 || this.rotate == 270) && 
26480                 (
26481                     width < this.minHeight || 
26482                     width > this.imageEl.OriginWidth || 
26483                     height < this.minWidth || 
26484                     height > this.imageEl.OriginHeight
26485                 )
26486         ){
26487             return false;
26488         }
26489         
26490         return true;
26491         
26492     },
26493     
26494     onRotateLeft : function(e)
26495     {   
26496         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26497             
26498             var minScale = this.thumbEl.getWidth() / this.minWidth;
26499             
26500             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26501             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26502             
26503             this.startScale = this.scale;
26504             
26505             while (this.getScaleLevel() < minScale){
26506             
26507                 this.scale = this.scale + 1;
26508                 
26509                 if(!this.zoomable()){
26510                     break;
26511                 }
26512                 
26513                 if(
26514                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26515                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26516                 ){
26517                     continue;
26518                 }
26519                 
26520                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26521
26522                 this.draw();
26523                 
26524                 return;
26525             }
26526             
26527             this.scale = this.startScale;
26528             
26529             this.onRotateFail();
26530             
26531             return false;
26532         }
26533         
26534         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26535
26536         if(this.isDocument){
26537             this.setThumbBoxSize();
26538             this.setThumbBoxPosition();
26539             this.setCanvasPosition();
26540         }
26541         
26542         this.draw();
26543         
26544         this.fireEvent('rotate', this, 'left');
26545         
26546     },
26547     
26548     onRotateRight : function(e)
26549     {
26550         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26551             
26552             var minScale = this.thumbEl.getWidth() / this.minWidth;
26553         
26554             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26555             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26556             
26557             this.startScale = this.scale;
26558             
26559             while (this.getScaleLevel() < minScale){
26560             
26561                 this.scale = this.scale + 1;
26562                 
26563                 if(!this.zoomable()){
26564                     break;
26565                 }
26566                 
26567                 if(
26568                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26569                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26570                 ){
26571                     continue;
26572                 }
26573                 
26574                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26575
26576                 this.draw();
26577                 
26578                 return;
26579             }
26580             
26581             this.scale = this.startScale;
26582             
26583             this.onRotateFail();
26584             
26585             return false;
26586         }
26587         
26588         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26589
26590         if(this.isDocument){
26591             this.setThumbBoxSize();
26592             this.setThumbBoxPosition();
26593             this.setCanvasPosition();
26594         }
26595         
26596         this.draw();
26597         
26598         this.fireEvent('rotate', this, 'right');
26599     },
26600     
26601     onRotateFail : function()
26602     {
26603         this.errorEl.show(true);
26604         
26605         var _this = this;
26606         
26607         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26608     },
26609     
26610     draw : function()
26611     {
26612         this.previewEl.dom.innerHTML = '';
26613         
26614         var canvasEl = document.createElement("canvas");
26615         
26616         var contextEl = canvasEl.getContext("2d");
26617         
26618         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26619         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26620         var center = this.imageEl.OriginWidth / 2;
26621         
26622         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26623             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26624             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26625             center = this.imageEl.OriginHeight / 2;
26626         }
26627         
26628         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26629         
26630         contextEl.translate(center, center);
26631         contextEl.rotate(this.rotate * Math.PI / 180);
26632
26633         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26634         
26635         this.canvasEl = document.createElement("canvas");
26636         
26637         this.contextEl = this.canvasEl.getContext("2d");
26638         
26639         switch (this.rotate) {
26640             case 0 :
26641                 
26642                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26643                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26644                 
26645                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26646                 
26647                 break;
26648             case 90 : 
26649                 
26650                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26651                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26652                 
26653                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26654                     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);
26655                     break;
26656                 }
26657                 
26658                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26659                 
26660                 break;
26661             case 180 :
26662                 
26663                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26664                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26665                 
26666                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26667                     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);
26668                     break;
26669                 }
26670                 
26671                 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);
26672                 
26673                 break;
26674             case 270 :
26675                 
26676                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26677                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26678         
26679                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26680                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26681                     break;
26682                 }
26683                 
26684                 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);
26685                 
26686                 break;
26687             default : 
26688                 break;
26689         }
26690         
26691         this.previewEl.appendChild(this.canvasEl);
26692         
26693         this.setCanvasPosition();
26694     },
26695     
26696     crop : function()
26697     {
26698         if(!this.canvasLoaded){
26699             return;
26700         }
26701         
26702         var imageCanvas = document.createElement("canvas");
26703         
26704         var imageContext = imageCanvas.getContext("2d");
26705         
26706         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26707         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26708         
26709         var center = imageCanvas.width / 2;
26710         
26711         imageContext.translate(center, center);
26712         
26713         imageContext.rotate(this.rotate * Math.PI / 180);
26714         
26715         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26716         
26717         var canvas = document.createElement("canvas");
26718         
26719         var context = canvas.getContext("2d");
26720                 
26721         canvas.width = this.minWidth;
26722         canvas.height = this.minHeight;
26723
26724         switch (this.rotate) {
26725             case 0 :
26726                 
26727                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26728                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26729                 
26730                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26731                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26732                 
26733                 var targetWidth = this.minWidth - 2 * x;
26734                 var targetHeight = this.minHeight - 2 * y;
26735                 
26736                 var scale = 1;
26737                 
26738                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26739                     scale = targetWidth / width;
26740                 }
26741                 
26742                 if(x > 0 && y == 0){
26743                     scale = targetHeight / height;
26744                 }
26745                 
26746                 if(x > 0 && y > 0){
26747                     scale = targetWidth / width;
26748                     
26749                     if(width < height){
26750                         scale = targetHeight / height;
26751                     }
26752                 }
26753                 
26754                 context.scale(scale, scale);
26755                 
26756                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26757                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26758
26759                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26760                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26761
26762                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26763                 
26764                 break;
26765             case 90 : 
26766                 
26767                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26768                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26769                 
26770                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26771                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26772                 
26773                 var targetWidth = this.minWidth - 2 * x;
26774                 var targetHeight = this.minHeight - 2 * y;
26775                 
26776                 var scale = 1;
26777                 
26778                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26779                     scale = targetWidth / width;
26780                 }
26781                 
26782                 if(x > 0 && y == 0){
26783                     scale = targetHeight / height;
26784                 }
26785                 
26786                 if(x > 0 && y > 0){
26787                     scale = targetWidth / width;
26788                     
26789                     if(width < height){
26790                         scale = targetHeight / height;
26791                     }
26792                 }
26793                 
26794                 context.scale(scale, scale);
26795                 
26796                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26797                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26798
26799                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26800                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26801                 
26802                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26803                 
26804                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26805                 
26806                 break;
26807             case 180 :
26808                 
26809                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26810                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26811                 
26812                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26813                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26814                 
26815                 var targetWidth = this.minWidth - 2 * x;
26816                 var targetHeight = this.minHeight - 2 * y;
26817                 
26818                 var scale = 1;
26819                 
26820                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26821                     scale = targetWidth / width;
26822                 }
26823                 
26824                 if(x > 0 && y == 0){
26825                     scale = targetHeight / height;
26826                 }
26827                 
26828                 if(x > 0 && y > 0){
26829                     scale = targetWidth / width;
26830                     
26831                     if(width < height){
26832                         scale = targetHeight / height;
26833                     }
26834                 }
26835                 
26836                 context.scale(scale, scale);
26837                 
26838                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26839                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26840
26841                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26842                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26843
26844                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26845                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26846                 
26847                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26848                 
26849                 break;
26850             case 270 :
26851                 
26852                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26853                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26854                 
26855                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26856                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26857                 
26858                 var targetWidth = this.minWidth - 2 * x;
26859                 var targetHeight = this.minHeight - 2 * y;
26860                 
26861                 var scale = 1;
26862                 
26863                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26864                     scale = targetWidth / width;
26865                 }
26866                 
26867                 if(x > 0 && y == 0){
26868                     scale = targetHeight / height;
26869                 }
26870                 
26871                 if(x > 0 && y > 0){
26872                     scale = targetWidth / width;
26873                     
26874                     if(width < height){
26875                         scale = targetHeight / height;
26876                     }
26877                 }
26878                 
26879                 context.scale(scale, scale);
26880                 
26881                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26882                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26883
26884                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26885                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26886                 
26887                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26888                 
26889                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26890                 
26891                 break;
26892             default : 
26893                 break;
26894         }
26895         
26896         this.cropData = canvas.toDataURL(this.cropType);
26897         
26898         if(this.fireEvent('crop', this, this.cropData) !== false){
26899             this.process(this.file, this.cropData);
26900         }
26901         
26902         return;
26903         
26904     },
26905     
26906     setThumbBoxSize : function()
26907     {
26908         var width, height;
26909         
26910         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26911             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26912             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26913             
26914             this.minWidth = width;
26915             this.minHeight = height;
26916             
26917             if(this.rotate == 90 || this.rotate == 270){
26918                 this.minWidth = height;
26919                 this.minHeight = width;
26920             }
26921         }
26922         
26923         height = 300;
26924         width = Math.ceil(this.minWidth * height / this.minHeight);
26925         
26926         if(this.minWidth > this.minHeight){
26927             width = 300;
26928             height = Math.ceil(this.minHeight * width / this.minWidth);
26929         }
26930         
26931         this.thumbEl.setStyle({
26932             width : width + 'px',
26933             height : height + 'px'
26934         });
26935
26936         return;
26937             
26938     },
26939     
26940     setThumbBoxPosition : function()
26941     {
26942         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26943         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26944         
26945         this.thumbEl.setLeft(x);
26946         this.thumbEl.setTop(y);
26947         
26948     },
26949     
26950     baseRotateLevel : function()
26951     {
26952         this.baseRotate = 1;
26953         
26954         if(
26955                 typeof(this.exif) != 'undefined' &&
26956                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26957                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26958         ){
26959             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26960         }
26961         
26962         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26963         
26964     },
26965     
26966     baseScaleLevel : function()
26967     {
26968         var width, height;
26969         
26970         if(this.isDocument){
26971             
26972             if(this.baseRotate == 6 || this.baseRotate == 8){
26973             
26974                 height = this.thumbEl.getHeight();
26975                 this.baseScale = height / this.imageEl.OriginWidth;
26976
26977                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26978                     width = this.thumbEl.getWidth();
26979                     this.baseScale = width / this.imageEl.OriginHeight;
26980                 }
26981
26982                 return;
26983             }
26984
26985             height = this.thumbEl.getHeight();
26986             this.baseScale = height / this.imageEl.OriginHeight;
26987
26988             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26989                 width = this.thumbEl.getWidth();
26990                 this.baseScale = width / this.imageEl.OriginWidth;
26991             }
26992
26993             return;
26994         }
26995         
26996         if(this.baseRotate == 6 || this.baseRotate == 8){
26997             
26998             width = this.thumbEl.getHeight();
26999             this.baseScale = width / this.imageEl.OriginHeight;
27000             
27001             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27002                 height = this.thumbEl.getWidth();
27003                 this.baseScale = height / this.imageEl.OriginHeight;
27004             }
27005             
27006             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27007                 height = this.thumbEl.getWidth();
27008                 this.baseScale = height / this.imageEl.OriginHeight;
27009                 
27010                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27011                     width = this.thumbEl.getHeight();
27012                     this.baseScale = width / this.imageEl.OriginWidth;
27013                 }
27014             }
27015             
27016             return;
27017         }
27018         
27019         width = this.thumbEl.getWidth();
27020         this.baseScale = width / this.imageEl.OriginWidth;
27021         
27022         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27023             height = this.thumbEl.getHeight();
27024             this.baseScale = height / this.imageEl.OriginHeight;
27025         }
27026         
27027         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27028             
27029             height = this.thumbEl.getHeight();
27030             this.baseScale = height / this.imageEl.OriginHeight;
27031             
27032             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27033                 width = this.thumbEl.getWidth();
27034                 this.baseScale = width / this.imageEl.OriginWidth;
27035             }
27036             
27037         }
27038         
27039         return;
27040     },
27041     
27042     getScaleLevel : function()
27043     {
27044         return this.baseScale * Math.pow(1.1, this.scale);
27045     },
27046     
27047     onTouchStart : function(e)
27048     {
27049         if(!this.canvasLoaded){
27050             this.beforeSelectFile(e);
27051             return;
27052         }
27053         
27054         var touches = e.browserEvent.touches;
27055         
27056         if(!touches){
27057             return;
27058         }
27059         
27060         if(touches.length == 1){
27061             this.onMouseDown(e);
27062             return;
27063         }
27064         
27065         if(touches.length != 2){
27066             return;
27067         }
27068         
27069         var coords = [];
27070         
27071         for(var i = 0, finger; finger = touches[i]; i++){
27072             coords.push(finger.pageX, finger.pageY);
27073         }
27074         
27075         var x = Math.pow(coords[0] - coords[2], 2);
27076         var y = Math.pow(coords[1] - coords[3], 2);
27077         
27078         this.startDistance = Math.sqrt(x + y);
27079         
27080         this.startScale = this.scale;
27081         
27082         this.pinching = true;
27083         this.dragable = false;
27084         
27085     },
27086     
27087     onTouchMove : function(e)
27088     {
27089         if(!this.pinching && !this.dragable){
27090             return;
27091         }
27092         
27093         var touches = e.browserEvent.touches;
27094         
27095         if(!touches){
27096             return;
27097         }
27098         
27099         if(this.dragable){
27100             this.onMouseMove(e);
27101             return;
27102         }
27103         
27104         var coords = [];
27105         
27106         for(var i = 0, finger; finger = touches[i]; i++){
27107             coords.push(finger.pageX, finger.pageY);
27108         }
27109         
27110         var x = Math.pow(coords[0] - coords[2], 2);
27111         var y = Math.pow(coords[1] - coords[3], 2);
27112         
27113         this.endDistance = Math.sqrt(x + y);
27114         
27115         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27116         
27117         if(!this.zoomable()){
27118             this.scale = this.startScale;
27119             return;
27120         }
27121         
27122         this.draw();
27123         
27124     },
27125     
27126     onTouchEnd : function(e)
27127     {
27128         this.pinching = false;
27129         this.dragable = false;
27130         
27131     },
27132     
27133     process : function(file, crop)
27134     {
27135         if(this.loadMask){
27136             this.maskEl.mask(this.loadingText);
27137         }
27138         
27139         this.xhr = new XMLHttpRequest();
27140         
27141         file.xhr = this.xhr;
27142
27143         this.xhr.open(this.method, this.url, true);
27144         
27145         var headers = {
27146             "Accept": "application/json",
27147             "Cache-Control": "no-cache",
27148             "X-Requested-With": "XMLHttpRequest"
27149         };
27150         
27151         for (var headerName in headers) {
27152             var headerValue = headers[headerName];
27153             if (headerValue) {
27154                 this.xhr.setRequestHeader(headerName, headerValue);
27155             }
27156         }
27157         
27158         var _this = this;
27159         
27160         this.xhr.onload = function()
27161         {
27162             _this.xhrOnLoad(_this.xhr);
27163         }
27164         
27165         this.xhr.onerror = function()
27166         {
27167             _this.xhrOnError(_this.xhr);
27168         }
27169         
27170         var formData = new FormData();
27171
27172         formData.append('returnHTML', 'NO');
27173         
27174         if(crop){
27175             formData.append('crop', crop);
27176         }
27177         
27178         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27179             formData.append(this.paramName, file, file.name);
27180         }
27181         
27182         if(typeof(file.filename) != 'undefined'){
27183             formData.append('filename', file.filename);
27184         }
27185         
27186         if(typeof(file.mimetype) != 'undefined'){
27187             formData.append('mimetype', file.mimetype);
27188         }
27189         
27190         if(this.fireEvent('arrange', this, formData) != false){
27191             this.xhr.send(formData);
27192         };
27193     },
27194     
27195     xhrOnLoad : function(xhr)
27196     {
27197         if(this.loadMask){
27198             this.maskEl.unmask();
27199         }
27200         
27201         if (xhr.readyState !== 4) {
27202             this.fireEvent('exception', this, xhr);
27203             return;
27204         }
27205
27206         var response = Roo.decode(xhr.responseText);
27207         
27208         if(!response.success){
27209             this.fireEvent('exception', this, xhr);
27210             return;
27211         }
27212         
27213         var response = Roo.decode(xhr.responseText);
27214         
27215         this.fireEvent('upload', this, response);
27216         
27217     },
27218     
27219     xhrOnError : function()
27220     {
27221         if(this.loadMask){
27222             this.maskEl.unmask();
27223         }
27224         
27225         Roo.log('xhr on error');
27226         
27227         var response = Roo.decode(xhr.responseText);
27228           
27229         Roo.log(response);
27230         
27231     },
27232     
27233     prepare : function(file)
27234     {   
27235         if(this.loadMask){
27236             this.maskEl.mask(this.loadingText);
27237         }
27238         
27239         this.file = false;
27240         this.exif = {};
27241         
27242         if(typeof(file) === 'string'){
27243             this.loadCanvas(file);
27244             return;
27245         }
27246         
27247         if(!file || !this.urlAPI){
27248             return;
27249         }
27250         
27251         this.file = file;
27252         this.cropType = file.type;
27253         
27254         var _this = this;
27255         
27256         if(this.fireEvent('prepare', this, this.file) != false){
27257             
27258             var reader = new FileReader();
27259             
27260             reader.onload = function (e) {
27261                 if (e.target.error) {
27262                     Roo.log(e.target.error);
27263                     return;
27264                 }
27265                 
27266                 var buffer = e.target.result,
27267                     dataView = new DataView(buffer),
27268                     offset = 2,
27269                     maxOffset = dataView.byteLength - 4,
27270                     markerBytes,
27271                     markerLength;
27272                 
27273                 if (dataView.getUint16(0) === 0xffd8) {
27274                     while (offset < maxOffset) {
27275                         markerBytes = dataView.getUint16(offset);
27276                         
27277                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27278                             markerLength = dataView.getUint16(offset + 2) + 2;
27279                             if (offset + markerLength > dataView.byteLength) {
27280                                 Roo.log('Invalid meta data: Invalid segment size.');
27281                                 break;
27282                             }
27283                             
27284                             if(markerBytes == 0xffe1){
27285                                 _this.parseExifData(
27286                                     dataView,
27287                                     offset,
27288                                     markerLength
27289                                 );
27290                             }
27291                             
27292                             offset += markerLength;
27293                             
27294                             continue;
27295                         }
27296                         
27297                         break;
27298                     }
27299                     
27300                 }
27301                 
27302                 var url = _this.urlAPI.createObjectURL(_this.file);
27303                 
27304                 _this.loadCanvas(url);
27305                 
27306                 return;
27307             }
27308             
27309             reader.readAsArrayBuffer(this.file);
27310             
27311         }
27312         
27313     },
27314     
27315     parseExifData : function(dataView, offset, length)
27316     {
27317         var tiffOffset = offset + 10,
27318             littleEndian,
27319             dirOffset;
27320     
27321         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27322             // No Exif data, might be XMP data instead
27323             return;
27324         }
27325         
27326         // Check for the ASCII code for "Exif" (0x45786966):
27327         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27328             // No Exif data, might be XMP data instead
27329             return;
27330         }
27331         if (tiffOffset + 8 > dataView.byteLength) {
27332             Roo.log('Invalid Exif data: Invalid segment size.');
27333             return;
27334         }
27335         // Check for the two null bytes:
27336         if (dataView.getUint16(offset + 8) !== 0x0000) {
27337             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27338             return;
27339         }
27340         // Check the byte alignment:
27341         switch (dataView.getUint16(tiffOffset)) {
27342         case 0x4949:
27343             littleEndian = true;
27344             break;
27345         case 0x4D4D:
27346             littleEndian = false;
27347             break;
27348         default:
27349             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27350             return;
27351         }
27352         // Check for the TIFF tag marker (0x002A):
27353         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27354             Roo.log('Invalid Exif data: Missing TIFF marker.');
27355             return;
27356         }
27357         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27358         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27359         
27360         this.parseExifTags(
27361             dataView,
27362             tiffOffset,
27363             tiffOffset + dirOffset,
27364             littleEndian
27365         );
27366     },
27367     
27368     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27369     {
27370         var tagsNumber,
27371             dirEndOffset,
27372             i;
27373         if (dirOffset + 6 > dataView.byteLength) {
27374             Roo.log('Invalid Exif data: Invalid directory offset.');
27375             return;
27376         }
27377         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27378         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27379         if (dirEndOffset + 4 > dataView.byteLength) {
27380             Roo.log('Invalid Exif data: Invalid directory size.');
27381             return;
27382         }
27383         for (i = 0; i < tagsNumber; i += 1) {
27384             this.parseExifTag(
27385                 dataView,
27386                 tiffOffset,
27387                 dirOffset + 2 + 12 * i, // tag offset
27388                 littleEndian
27389             );
27390         }
27391         // Return the offset to the next directory:
27392         return dataView.getUint32(dirEndOffset, littleEndian);
27393     },
27394     
27395     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
27396     {
27397         var tag = dataView.getUint16(offset, littleEndian);
27398         
27399         this.exif[tag] = this.getExifValue(
27400             dataView,
27401             tiffOffset,
27402             offset,
27403             dataView.getUint16(offset + 2, littleEndian), // tag type
27404             dataView.getUint32(offset + 4, littleEndian), // tag length
27405             littleEndian
27406         );
27407     },
27408     
27409     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27410     {
27411         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27412             tagSize,
27413             dataOffset,
27414             values,
27415             i,
27416             str,
27417             c;
27418     
27419         if (!tagType) {
27420             Roo.log('Invalid Exif data: Invalid tag type.');
27421             return;
27422         }
27423         
27424         tagSize = tagType.size * length;
27425         // Determine if the value is contained in the dataOffset bytes,
27426         // or if the value at the dataOffset is a pointer to the actual data:
27427         dataOffset = tagSize > 4 ?
27428                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27429         if (dataOffset + tagSize > dataView.byteLength) {
27430             Roo.log('Invalid Exif data: Invalid data offset.');
27431             return;
27432         }
27433         if (length === 1) {
27434             return tagType.getValue(dataView, dataOffset, littleEndian);
27435         }
27436         values = [];
27437         for (i = 0; i < length; i += 1) {
27438             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27439         }
27440         
27441         if (tagType.ascii) {
27442             str = '';
27443             // Concatenate the chars:
27444             for (i = 0; i < values.length; i += 1) {
27445                 c = values[i];
27446                 // Ignore the terminating NULL byte(s):
27447                 if (c === '\u0000') {
27448                     break;
27449                 }
27450                 str += c;
27451             }
27452             return str;
27453         }
27454         return values;
27455     }
27456     
27457 });
27458
27459 Roo.apply(Roo.bootstrap.UploadCropbox, {
27460     tags : {
27461         'Orientation': 0x0112
27462     },
27463     
27464     Orientation: {
27465             1: 0, //'top-left',
27466 //            2: 'top-right',
27467             3: 180, //'bottom-right',
27468 //            4: 'bottom-left',
27469 //            5: 'left-top',
27470             6: 90, //'right-top',
27471 //            7: 'right-bottom',
27472             8: 270 //'left-bottom'
27473     },
27474     
27475     exifTagTypes : {
27476         // byte, 8-bit unsigned int:
27477         1: {
27478             getValue: function (dataView, dataOffset) {
27479                 return dataView.getUint8(dataOffset);
27480             },
27481             size: 1
27482         },
27483         // ascii, 8-bit byte:
27484         2: {
27485             getValue: function (dataView, dataOffset) {
27486                 return String.fromCharCode(dataView.getUint8(dataOffset));
27487             },
27488             size: 1,
27489             ascii: true
27490         },
27491         // short, 16 bit int:
27492         3: {
27493             getValue: function (dataView, dataOffset, littleEndian) {
27494                 return dataView.getUint16(dataOffset, littleEndian);
27495             },
27496             size: 2
27497         },
27498         // long, 32 bit int:
27499         4: {
27500             getValue: function (dataView, dataOffset, littleEndian) {
27501                 return dataView.getUint32(dataOffset, littleEndian);
27502             },
27503             size: 4
27504         },
27505         // rational = two long values, first is numerator, second is denominator:
27506         5: {
27507             getValue: function (dataView, dataOffset, littleEndian) {
27508                 return dataView.getUint32(dataOffset, littleEndian) /
27509                     dataView.getUint32(dataOffset + 4, littleEndian);
27510             },
27511             size: 8
27512         },
27513         // slong, 32 bit signed int:
27514         9: {
27515             getValue: function (dataView, dataOffset, littleEndian) {
27516                 return dataView.getInt32(dataOffset, littleEndian);
27517             },
27518             size: 4
27519         },
27520         // srational, two slongs, first is numerator, second is denominator:
27521         10: {
27522             getValue: function (dataView, dataOffset, littleEndian) {
27523                 return dataView.getInt32(dataOffset, littleEndian) /
27524                     dataView.getInt32(dataOffset + 4, littleEndian);
27525             },
27526             size: 8
27527         }
27528     },
27529     
27530     footer : {
27531         STANDARD : [
27532             {
27533                 tag : 'div',
27534                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27535                 action : 'rotate-left',
27536                 cn : [
27537                     {
27538                         tag : 'button',
27539                         cls : 'btn btn-default',
27540                         html : '<i class="fa fa-undo"></i>'
27541                     }
27542                 ]
27543             },
27544             {
27545                 tag : 'div',
27546                 cls : 'btn-group roo-upload-cropbox-picture',
27547                 action : 'picture',
27548                 cn : [
27549                     {
27550                         tag : 'button',
27551                         cls : 'btn btn-default',
27552                         html : '<i class="fa fa-picture-o"></i>'
27553                     }
27554                 ]
27555             },
27556             {
27557                 tag : 'div',
27558                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27559                 action : 'rotate-right',
27560                 cn : [
27561                     {
27562                         tag : 'button',
27563                         cls : 'btn btn-default',
27564                         html : '<i class="fa fa-repeat"></i>'
27565                     }
27566                 ]
27567             }
27568         ],
27569         DOCUMENT : [
27570             {
27571                 tag : 'div',
27572                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27573                 action : 'rotate-left',
27574                 cn : [
27575                     {
27576                         tag : 'button',
27577                         cls : 'btn btn-default',
27578                         html : '<i class="fa fa-undo"></i>'
27579                     }
27580                 ]
27581             },
27582             {
27583                 tag : 'div',
27584                 cls : 'btn-group roo-upload-cropbox-download',
27585                 action : 'download',
27586                 cn : [
27587                     {
27588                         tag : 'button',
27589                         cls : 'btn btn-default',
27590                         html : '<i class="fa fa-download"></i>'
27591                     }
27592                 ]
27593             },
27594             {
27595                 tag : 'div',
27596                 cls : 'btn-group roo-upload-cropbox-crop',
27597                 action : 'crop',
27598                 cn : [
27599                     {
27600                         tag : 'button',
27601                         cls : 'btn btn-default',
27602                         html : '<i class="fa fa-crop"></i>'
27603                     }
27604                 ]
27605             },
27606             {
27607                 tag : 'div',
27608                 cls : 'btn-group roo-upload-cropbox-trash',
27609                 action : 'trash',
27610                 cn : [
27611                     {
27612                         tag : 'button',
27613                         cls : 'btn btn-default',
27614                         html : '<i class="fa fa-trash"></i>'
27615                     }
27616                 ]
27617             },
27618             {
27619                 tag : 'div',
27620                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27621                 action : 'rotate-right',
27622                 cn : [
27623                     {
27624                         tag : 'button',
27625                         cls : 'btn btn-default',
27626                         html : '<i class="fa fa-repeat"></i>'
27627                     }
27628                 ]
27629             }
27630         ],
27631         ROTATOR : [
27632             {
27633                 tag : 'div',
27634                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27635                 action : 'rotate-left',
27636                 cn : [
27637                     {
27638                         tag : 'button',
27639                         cls : 'btn btn-default',
27640                         html : '<i class="fa fa-undo"></i>'
27641                     }
27642                 ]
27643             },
27644             {
27645                 tag : 'div',
27646                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27647                 action : 'rotate-right',
27648                 cn : [
27649                     {
27650                         tag : 'button',
27651                         cls : 'btn btn-default',
27652                         html : '<i class="fa fa-repeat"></i>'
27653                     }
27654                 ]
27655             }
27656         ]
27657     }
27658 });
27659
27660 /*
27661 * Licence: LGPL
27662 */
27663
27664 /**
27665  * @class Roo.bootstrap.DocumentManager
27666  * @extends Roo.bootstrap.Component
27667  * Bootstrap DocumentManager class
27668  * @cfg {String} paramName default 'imageUpload'
27669  * @cfg {String} toolTipName default 'filename'
27670  * @cfg {String} method default POST
27671  * @cfg {String} url action url
27672  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27673  * @cfg {Boolean} multiple multiple upload default true
27674  * @cfg {Number} thumbSize default 300
27675  * @cfg {String} fieldLabel
27676  * @cfg {Number} labelWidth default 4
27677  * @cfg {String} labelAlign (left|top) default left
27678  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27679 * @cfg {Number} labellg set the width of label (1-12)
27680  * @cfg {Number} labelmd set the width of label (1-12)
27681  * @cfg {Number} labelsm set the width of label (1-12)
27682  * @cfg {Number} labelxs set the width of label (1-12)
27683  * 
27684  * @constructor
27685  * Create a new DocumentManager
27686  * @param {Object} config The config object
27687  */
27688
27689 Roo.bootstrap.DocumentManager = function(config){
27690     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27691     
27692     this.files = [];
27693     this.delegates = [];
27694     
27695     this.addEvents({
27696         /**
27697          * @event initial
27698          * Fire when initial the DocumentManager
27699          * @param {Roo.bootstrap.DocumentManager} this
27700          */
27701         "initial" : true,
27702         /**
27703          * @event inspect
27704          * inspect selected file
27705          * @param {Roo.bootstrap.DocumentManager} this
27706          * @param {File} file
27707          */
27708         "inspect" : true,
27709         /**
27710          * @event exception
27711          * Fire when xhr load exception
27712          * @param {Roo.bootstrap.DocumentManager} this
27713          * @param {XMLHttpRequest} xhr
27714          */
27715         "exception" : true,
27716         /**
27717          * @event afterupload
27718          * Fire when xhr load exception
27719          * @param {Roo.bootstrap.DocumentManager} this
27720          * @param {XMLHttpRequest} xhr
27721          */
27722         "afterupload" : true,
27723         /**
27724          * @event prepare
27725          * prepare the form data
27726          * @param {Roo.bootstrap.DocumentManager} this
27727          * @param {Object} formData
27728          */
27729         "prepare" : true,
27730         /**
27731          * @event remove
27732          * Fire when remove the file
27733          * @param {Roo.bootstrap.DocumentManager} this
27734          * @param {Object} file
27735          */
27736         "remove" : true,
27737         /**
27738          * @event refresh
27739          * Fire after refresh the file
27740          * @param {Roo.bootstrap.DocumentManager} this
27741          */
27742         "refresh" : true,
27743         /**
27744          * @event click
27745          * Fire after click the image
27746          * @param {Roo.bootstrap.DocumentManager} this
27747          * @param {Object} file
27748          */
27749         "click" : true,
27750         /**
27751          * @event edit
27752          * Fire when upload a image and editable set to true
27753          * @param {Roo.bootstrap.DocumentManager} this
27754          * @param {Object} file
27755          */
27756         "edit" : true,
27757         /**
27758          * @event beforeselectfile
27759          * Fire before select file
27760          * @param {Roo.bootstrap.DocumentManager} this
27761          */
27762         "beforeselectfile" : true,
27763         /**
27764          * @event process
27765          * Fire before process file
27766          * @param {Roo.bootstrap.DocumentManager} this
27767          * @param {Object} file
27768          */
27769         "process" : true
27770         
27771     });
27772 };
27773
27774 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
27775     
27776     boxes : 0,
27777     inputName : '',
27778     thumbSize : 300,
27779     multiple : true,
27780     files : false,
27781     method : 'POST',
27782     url : '',
27783     paramName : 'imageUpload',
27784     toolTipName : 'filename',
27785     fieldLabel : '',
27786     labelWidth : 4,
27787     labelAlign : 'left',
27788     editable : true,
27789     delegates : false,
27790     xhr : false, 
27791     
27792     labellg : 0,
27793     labelmd : 0,
27794     labelsm : 0,
27795     labelxs : 0,
27796     
27797     getAutoCreate : function()
27798     {   
27799         var managerWidget = {
27800             tag : 'div',
27801             cls : 'roo-document-manager',
27802             cn : [
27803                 {
27804                     tag : 'input',
27805                     cls : 'roo-document-manager-selector',
27806                     type : 'file'
27807                 },
27808                 {
27809                     tag : 'div',
27810                     cls : 'roo-document-manager-uploader',
27811                     cn : [
27812                         {
27813                             tag : 'div',
27814                             cls : 'roo-document-manager-upload-btn',
27815                             html : '<i class="fa fa-plus"></i>'
27816                         }
27817                     ]
27818                     
27819                 }
27820             ]
27821         };
27822         
27823         var content = [
27824             {
27825                 tag : 'div',
27826                 cls : 'column col-md-12',
27827                 cn : managerWidget
27828             }
27829         ];
27830         
27831         if(this.fieldLabel.length){
27832             
27833             content = [
27834                 {
27835                     tag : 'div',
27836                     cls : 'column col-md-12',
27837                     html : this.fieldLabel
27838                 },
27839                 {
27840                     tag : 'div',
27841                     cls : 'column col-md-12',
27842                     cn : managerWidget
27843                 }
27844             ];
27845
27846             if(this.labelAlign == 'left'){
27847                 content = [
27848                     {
27849                         tag : 'div',
27850                         cls : 'column',
27851                         html : this.fieldLabel
27852                     },
27853                     {
27854                         tag : 'div',
27855                         cls : 'column',
27856                         cn : managerWidget
27857                     }
27858                 ];
27859                 
27860                 if(this.labelWidth > 12){
27861                     content[0].style = "width: " + this.labelWidth + 'px';
27862                 }
27863
27864                 if(this.labelWidth < 13 && this.labelmd == 0){
27865                     this.labelmd = this.labelWidth;
27866                 }
27867
27868                 if(this.labellg > 0){
27869                     content[0].cls += ' col-lg-' + this.labellg;
27870                     content[1].cls += ' col-lg-' + (12 - this.labellg);
27871                 }
27872
27873                 if(this.labelmd > 0){
27874                     content[0].cls += ' col-md-' + this.labelmd;
27875                     content[1].cls += ' col-md-' + (12 - this.labelmd);
27876                 }
27877
27878                 if(this.labelsm > 0){
27879                     content[0].cls += ' col-sm-' + this.labelsm;
27880                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
27881                 }
27882
27883                 if(this.labelxs > 0){
27884                     content[0].cls += ' col-xs-' + this.labelxs;
27885                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
27886                 }
27887                 
27888             }
27889         }
27890         
27891         var cfg = {
27892             tag : 'div',
27893             cls : 'row clearfix',
27894             cn : content
27895         };
27896         
27897         return cfg;
27898         
27899     },
27900     
27901     initEvents : function()
27902     {
27903         this.managerEl = this.el.select('.roo-document-manager', true).first();
27904         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27905         
27906         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27907         this.selectorEl.hide();
27908         
27909         if(this.multiple){
27910             this.selectorEl.attr('multiple', 'multiple');
27911         }
27912         
27913         this.selectorEl.on('change', this.onFileSelected, this);
27914         
27915         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27916         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27917         
27918         this.uploader.on('click', this.onUploaderClick, this);
27919         
27920         this.renderProgressDialog();
27921         
27922         var _this = this;
27923         
27924         window.addEventListener("resize", function() { _this.refresh(); } );
27925         
27926         this.fireEvent('initial', this);
27927     },
27928     
27929     renderProgressDialog : function()
27930     {
27931         var _this = this;
27932         
27933         this.progressDialog = new Roo.bootstrap.Modal({
27934             cls : 'roo-document-manager-progress-dialog',
27935             allow_close : false,
27936             title : '',
27937             buttons : [
27938                 {
27939                     name  :'cancel',
27940                     weight : 'danger',
27941                     html : 'Cancel'
27942                 }
27943             ], 
27944             listeners : { 
27945                 btnclick : function() {
27946                     _this.uploadCancel();
27947                     this.hide();
27948                 }
27949             }
27950         });
27951          
27952         this.progressDialog.render(Roo.get(document.body));
27953          
27954         this.progress = new Roo.bootstrap.Progress({
27955             cls : 'roo-document-manager-progress',
27956             active : true,
27957             striped : true
27958         });
27959         
27960         this.progress.render(this.progressDialog.getChildContainer());
27961         
27962         this.progressBar = new Roo.bootstrap.ProgressBar({
27963             cls : 'roo-document-manager-progress-bar',
27964             aria_valuenow : 0,
27965             aria_valuemin : 0,
27966             aria_valuemax : 12,
27967             panel : 'success'
27968         });
27969         
27970         this.progressBar.render(this.progress.getChildContainer());
27971     },
27972     
27973     onUploaderClick : function(e)
27974     {
27975         e.preventDefault();
27976      
27977         if(this.fireEvent('beforeselectfile', this) != false){
27978             this.selectorEl.dom.click();
27979         }
27980         
27981     },
27982     
27983     onFileSelected : function(e)
27984     {
27985         e.preventDefault();
27986         
27987         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27988             return;
27989         }
27990         
27991         Roo.each(this.selectorEl.dom.files, function(file){
27992             if(this.fireEvent('inspect', this, file) != false){
27993                 this.files.push(file);
27994             }
27995         }, this);
27996         
27997         this.queue();
27998         
27999     },
28000     
28001     queue : function()
28002     {
28003         this.selectorEl.dom.value = '';
28004         
28005         if(!this.files.length){
28006             return;
28007         }
28008         
28009         if(this.boxes > 0 && this.files.length > this.boxes){
28010             this.files = this.files.slice(0, this.boxes);
28011         }
28012         
28013         this.uploader.show();
28014         
28015         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28016             this.uploader.hide();
28017         }
28018         
28019         var _this = this;
28020         
28021         var files = [];
28022         
28023         var docs = [];
28024         
28025         Roo.each(this.files, function(file){
28026             
28027             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28028                 var f = this.renderPreview(file);
28029                 files.push(f);
28030                 return;
28031             }
28032             
28033             if(file.type.indexOf('image') != -1){
28034                 this.delegates.push(
28035                     (function(){
28036                         _this.process(file);
28037                     }).createDelegate(this)
28038                 );
28039         
28040                 return;
28041             }
28042             
28043             docs.push(
28044                 (function(){
28045                     _this.process(file);
28046                 }).createDelegate(this)
28047             );
28048             
28049         }, this);
28050         
28051         this.files = files;
28052         
28053         this.delegates = this.delegates.concat(docs);
28054         
28055         if(!this.delegates.length){
28056             this.refresh();
28057             return;
28058         }
28059         
28060         this.progressBar.aria_valuemax = this.delegates.length;
28061         
28062         this.arrange();
28063         
28064         return;
28065     },
28066     
28067     arrange : function()
28068     {
28069         if(!this.delegates.length){
28070             this.progressDialog.hide();
28071             this.refresh();
28072             return;
28073         }
28074         
28075         var delegate = this.delegates.shift();
28076         
28077         this.progressDialog.show();
28078         
28079         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28080         
28081         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28082         
28083         delegate();
28084     },
28085     
28086     refresh : function()
28087     {
28088         this.uploader.show();
28089         
28090         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28091             this.uploader.hide();
28092         }
28093         
28094         Roo.isTouch ? this.closable(false) : this.closable(true);
28095         
28096         this.fireEvent('refresh', this);
28097     },
28098     
28099     onRemove : function(e, el, o)
28100     {
28101         e.preventDefault();
28102         
28103         this.fireEvent('remove', this, o);
28104         
28105     },
28106     
28107     remove : function(o)
28108     {
28109         var files = [];
28110         
28111         Roo.each(this.files, function(file){
28112             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28113                 files.push(file);
28114                 return;
28115             }
28116
28117             o.target.remove();
28118
28119         }, this);
28120         
28121         this.files = files;
28122         
28123         this.refresh();
28124     },
28125     
28126     clear : function()
28127     {
28128         Roo.each(this.files, function(file){
28129             if(!file.target){
28130                 return;
28131             }
28132             
28133             file.target.remove();
28134
28135         }, this);
28136         
28137         this.files = [];
28138         
28139         this.refresh();
28140     },
28141     
28142     onClick : function(e, el, o)
28143     {
28144         e.preventDefault();
28145         
28146         this.fireEvent('click', this, o);
28147         
28148     },
28149     
28150     closable : function(closable)
28151     {
28152         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28153             
28154             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28155             
28156             if(closable){
28157                 el.show();
28158                 return;
28159             }
28160             
28161             el.hide();
28162             
28163         }, this);
28164     },
28165     
28166     xhrOnLoad : function(xhr)
28167     {
28168         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28169             el.remove();
28170         }, this);
28171         
28172         if (xhr.readyState !== 4) {
28173             this.arrange();
28174             this.fireEvent('exception', this, xhr);
28175             return;
28176         }
28177
28178         var response = Roo.decode(xhr.responseText);
28179         
28180         if(!response.success){
28181             this.arrange();
28182             this.fireEvent('exception', this, xhr);
28183             return;
28184         }
28185         
28186         var file = this.renderPreview(response.data);
28187         
28188         this.files.push(file);
28189         
28190         this.arrange();
28191         
28192         this.fireEvent('afterupload', this, xhr);
28193         
28194     },
28195     
28196     xhrOnError : function(xhr)
28197     {
28198         Roo.log('xhr on error');
28199         
28200         var response = Roo.decode(xhr.responseText);
28201           
28202         Roo.log(response);
28203         
28204         this.arrange();
28205     },
28206     
28207     process : function(file)
28208     {
28209         if(this.fireEvent('process', this, file) !== false){
28210             if(this.editable && file.type.indexOf('image') != -1){
28211                 this.fireEvent('edit', this, file);
28212                 return;
28213             }
28214
28215             this.uploadStart(file, false);
28216
28217             return;
28218         }
28219         
28220     },
28221     
28222     uploadStart : function(file, crop)
28223     {
28224         this.xhr = new XMLHttpRequest();
28225         
28226         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28227             this.arrange();
28228             return;
28229         }
28230         
28231         file.xhr = this.xhr;
28232             
28233         this.managerEl.createChild({
28234             tag : 'div',
28235             cls : 'roo-document-manager-loading',
28236             cn : [
28237                 {
28238                     tag : 'div',
28239                     tooltip : file.name,
28240                     cls : 'roo-document-manager-thumb',
28241                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28242                 }
28243             ]
28244
28245         });
28246
28247         this.xhr.open(this.method, this.url, true);
28248         
28249         var headers = {
28250             "Accept": "application/json",
28251             "Cache-Control": "no-cache",
28252             "X-Requested-With": "XMLHttpRequest"
28253         };
28254         
28255         for (var headerName in headers) {
28256             var headerValue = headers[headerName];
28257             if (headerValue) {
28258                 this.xhr.setRequestHeader(headerName, headerValue);
28259             }
28260         }
28261         
28262         var _this = this;
28263         
28264         this.xhr.onload = function()
28265         {
28266             _this.xhrOnLoad(_this.xhr);
28267         }
28268         
28269         this.xhr.onerror = function()
28270         {
28271             _this.xhrOnError(_this.xhr);
28272         }
28273         
28274         var formData = new FormData();
28275
28276         formData.append('returnHTML', 'NO');
28277         
28278         if(crop){
28279             formData.append('crop', crop);
28280         }
28281         
28282         formData.append(this.paramName, file, file.name);
28283         
28284         var options = {
28285             file : file, 
28286             manually : false
28287         };
28288         
28289         if(this.fireEvent('prepare', this, formData, options) != false){
28290             
28291             if(options.manually){
28292                 return;
28293             }
28294             
28295             this.xhr.send(formData);
28296             return;
28297         };
28298         
28299         this.uploadCancel();
28300     },
28301     
28302     uploadCancel : function()
28303     {
28304         if (this.xhr) {
28305             this.xhr.abort();
28306         }
28307         
28308         this.delegates = [];
28309         
28310         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28311             el.remove();
28312         }, this);
28313         
28314         this.arrange();
28315     },
28316     
28317     renderPreview : function(file)
28318     {
28319         if(typeof(file.target) != 'undefined' && file.target){
28320             return file;
28321         }
28322         
28323         var previewEl = this.managerEl.createChild({
28324             tag : 'div',
28325             cls : 'roo-document-manager-preview',
28326             cn : [
28327                 {
28328                     tag : 'div',
28329                     tooltip : file[this.toolTipName],
28330                     cls : 'roo-document-manager-thumb',
28331                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
28332                 },
28333                 {
28334                     tag : 'button',
28335                     cls : 'close',
28336                     html : '<i class="fa fa-times-circle"></i>'
28337                 }
28338             ]
28339         });
28340
28341         var close = previewEl.select('button.close', true).first();
28342
28343         close.on('click', this.onRemove, this, file);
28344
28345         file.target = previewEl;
28346
28347         var image = previewEl.select('img', true).first();
28348         
28349         var _this = this;
28350         
28351         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28352         
28353         image.on('click', this.onClick, this, file);
28354         
28355         return file;
28356         
28357     },
28358     
28359     onPreviewLoad : function(file, image)
28360     {
28361         if(typeof(file.target) == 'undefined' || !file.target){
28362             return;
28363         }
28364         
28365         var width = image.dom.naturalWidth || image.dom.width;
28366         var height = image.dom.naturalHeight || image.dom.height;
28367         
28368         if(width > height){
28369             file.target.addClass('wide');
28370             return;
28371         }
28372         
28373         file.target.addClass('tall');
28374         return;
28375         
28376     },
28377     
28378     uploadFromSource : function(file, crop)
28379     {
28380         this.xhr = new XMLHttpRequest();
28381         
28382         this.managerEl.createChild({
28383             tag : 'div',
28384             cls : 'roo-document-manager-loading',
28385             cn : [
28386                 {
28387                     tag : 'div',
28388                     tooltip : file.name,
28389                     cls : 'roo-document-manager-thumb',
28390                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28391                 }
28392             ]
28393
28394         });
28395
28396         this.xhr.open(this.method, this.url, true);
28397         
28398         var headers = {
28399             "Accept": "application/json",
28400             "Cache-Control": "no-cache",
28401             "X-Requested-With": "XMLHttpRequest"
28402         };
28403         
28404         for (var headerName in headers) {
28405             var headerValue = headers[headerName];
28406             if (headerValue) {
28407                 this.xhr.setRequestHeader(headerName, headerValue);
28408             }
28409         }
28410         
28411         var _this = this;
28412         
28413         this.xhr.onload = function()
28414         {
28415             _this.xhrOnLoad(_this.xhr);
28416         }
28417         
28418         this.xhr.onerror = function()
28419         {
28420             _this.xhrOnError(_this.xhr);
28421         }
28422         
28423         var formData = new FormData();
28424
28425         formData.append('returnHTML', 'NO');
28426         
28427         formData.append('crop', crop);
28428         
28429         if(typeof(file.filename) != 'undefined'){
28430             formData.append('filename', file.filename);
28431         }
28432         
28433         if(typeof(file.mimetype) != 'undefined'){
28434             formData.append('mimetype', file.mimetype);
28435         }
28436         
28437         if(this.fireEvent('prepare', this, formData) != false){
28438             this.xhr.send(formData);
28439         };
28440     }
28441 });
28442
28443 /*
28444 * Licence: LGPL
28445 */
28446
28447 /**
28448  * @class Roo.bootstrap.DocumentViewer
28449  * @extends Roo.bootstrap.Component
28450  * Bootstrap DocumentViewer class
28451  * @cfg {Boolean} showDownload (true|false) show download button (default true)
28452  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28453  * 
28454  * @constructor
28455  * Create a new DocumentViewer
28456  * @param {Object} config The config object
28457  */
28458
28459 Roo.bootstrap.DocumentViewer = function(config){
28460     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28461     
28462     this.addEvents({
28463         /**
28464          * @event initial
28465          * Fire after initEvent
28466          * @param {Roo.bootstrap.DocumentViewer} this
28467          */
28468         "initial" : true,
28469         /**
28470          * @event click
28471          * Fire after click
28472          * @param {Roo.bootstrap.DocumentViewer} this
28473          */
28474         "click" : true,
28475         /**
28476          * @event download
28477          * Fire after download button
28478          * @param {Roo.bootstrap.DocumentViewer} this
28479          */
28480         "download" : true,
28481         /**
28482          * @event trash
28483          * Fire after trash button
28484          * @param {Roo.bootstrap.DocumentViewer} this
28485          */
28486         "trash" : true
28487         
28488     });
28489 };
28490
28491 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
28492     
28493     showDownload : true,
28494     
28495     showTrash : true,
28496     
28497     getAutoCreate : function()
28498     {
28499         var cfg = {
28500             tag : 'div',
28501             cls : 'roo-document-viewer',
28502             cn : [
28503                 {
28504                     tag : 'div',
28505                     cls : 'roo-document-viewer-body',
28506                     cn : [
28507                         {
28508                             tag : 'div',
28509                             cls : 'roo-document-viewer-thumb',
28510                             cn : [
28511                                 {
28512                                     tag : 'img',
28513                                     cls : 'roo-document-viewer-image'
28514                                 }
28515                             ]
28516                         }
28517                     ]
28518                 },
28519                 {
28520                     tag : 'div',
28521                     cls : 'roo-document-viewer-footer',
28522                     cn : {
28523                         tag : 'div',
28524                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
28525                         cn : [
28526                             {
28527                                 tag : 'div',
28528                                 cls : 'btn-group roo-document-viewer-download',
28529                                 cn : [
28530                                     {
28531                                         tag : 'button',
28532                                         cls : 'btn btn-default',
28533                                         html : '<i class="fa fa-download"></i>'
28534                                     }
28535                                 ]
28536                             },
28537                             {
28538                                 tag : 'div',
28539                                 cls : 'btn-group roo-document-viewer-trash',
28540                                 cn : [
28541                                     {
28542                                         tag : 'button',
28543                                         cls : 'btn btn-default',
28544                                         html : '<i class="fa fa-trash"></i>'
28545                                     }
28546                                 ]
28547                             }
28548                         ]
28549                     }
28550                 }
28551             ]
28552         };
28553         
28554         return cfg;
28555     },
28556     
28557     initEvents : function()
28558     {
28559         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
28560         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
28561         
28562         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
28563         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
28564         
28565         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
28566         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
28567         
28568         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
28569         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
28570         
28571         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
28572         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
28573         
28574         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
28575         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
28576         
28577         this.bodyEl.on('click', this.onClick, this);
28578         this.downloadBtn.on('click', this.onDownload, this);
28579         this.trashBtn.on('click', this.onTrash, this);
28580         
28581         this.downloadBtn.hide();
28582         this.trashBtn.hide();
28583         
28584         if(this.showDownload){
28585             this.downloadBtn.show();
28586         }
28587         
28588         if(this.showTrash){
28589             this.trashBtn.show();
28590         }
28591         
28592         if(!this.showDownload && !this.showTrash) {
28593             this.footerEl.hide();
28594         }
28595         
28596     },
28597     
28598     initial : function()
28599     {
28600         this.fireEvent('initial', this);
28601         
28602     },
28603     
28604     onClick : function(e)
28605     {
28606         e.preventDefault();
28607         
28608         this.fireEvent('click', this);
28609     },
28610     
28611     onDownload : function(e)
28612     {
28613         e.preventDefault();
28614         
28615         this.fireEvent('download', this);
28616     },
28617     
28618     onTrash : function(e)
28619     {
28620         e.preventDefault();
28621         
28622         this.fireEvent('trash', this);
28623     }
28624     
28625 });
28626 /*
28627  * - LGPL
28628  *
28629  * nav progress bar
28630  * 
28631  */
28632
28633 /**
28634  * @class Roo.bootstrap.NavProgressBar
28635  * @extends Roo.bootstrap.Component
28636  * Bootstrap NavProgressBar class
28637  * 
28638  * @constructor
28639  * Create a new nav progress bar
28640  * @param {Object} config The config object
28641  */
28642
28643 Roo.bootstrap.NavProgressBar = function(config){
28644     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
28645
28646     this.bullets = this.bullets || [];
28647    
28648 //    Roo.bootstrap.NavProgressBar.register(this);
28649      this.addEvents({
28650         /**
28651              * @event changed
28652              * Fires when the active item changes
28653              * @param {Roo.bootstrap.NavProgressBar} this
28654              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28655              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
28656          */
28657         'changed': true
28658      });
28659     
28660 };
28661
28662 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
28663     
28664     bullets : [],
28665     barItems : [],
28666     
28667     getAutoCreate : function()
28668     {
28669         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28670         
28671         cfg = {
28672             tag : 'div',
28673             cls : 'roo-navigation-bar-group',
28674             cn : [
28675                 {
28676                     tag : 'div',
28677                     cls : 'roo-navigation-top-bar'
28678                 },
28679                 {
28680                     tag : 'div',
28681                     cls : 'roo-navigation-bullets-bar',
28682                     cn : [
28683                         {
28684                             tag : 'ul',
28685                             cls : 'roo-navigation-bar'
28686                         }
28687                     ]
28688                 },
28689                 
28690                 {
28691                     tag : 'div',
28692                     cls : 'roo-navigation-bottom-bar'
28693                 }
28694             ]
28695             
28696         };
28697         
28698         return cfg;
28699         
28700     },
28701     
28702     initEvents: function() 
28703     {
28704         
28705     },
28706     
28707     onRender : function(ct, position) 
28708     {
28709         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28710         
28711         if(this.bullets.length){
28712             Roo.each(this.bullets, function(b){
28713                this.addItem(b);
28714             }, this);
28715         }
28716         
28717         this.format();
28718         
28719     },
28720     
28721     addItem : function(cfg)
28722     {
28723         var item = new Roo.bootstrap.NavProgressItem(cfg);
28724         
28725         item.parentId = this.id;
28726         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28727         
28728         if(cfg.html){
28729             var top = new Roo.bootstrap.Element({
28730                 tag : 'div',
28731                 cls : 'roo-navigation-bar-text'
28732             });
28733             
28734             var bottom = new Roo.bootstrap.Element({
28735                 tag : 'div',
28736                 cls : 'roo-navigation-bar-text'
28737             });
28738             
28739             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28740             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28741             
28742             var topText = new Roo.bootstrap.Element({
28743                 tag : 'span',
28744                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28745             });
28746             
28747             var bottomText = new Roo.bootstrap.Element({
28748                 tag : 'span',
28749                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28750             });
28751             
28752             topText.onRender(top.el, null);
28753             bottomText.onRender(bottom.el, null);
28754             
28755             item.topEl = top;
28756             item.bottomEl = bottom;
28757         }
28758         
28759         this.barItems.push(item);
28760         
28761         return item;
28762     },
28763     
28764     getActive : function()
28765     {
28766         var active = false;
28767         
28768         Roo.each(this.barItems, function(v){
28769             
28770             if (!v.isActive()) {
28771                 return;
28772             }
28773             
28774             active = v;
28775             return false;
28776             
28777         });
28778         
28779         return active;
28780     },
28781     
28782     setActiveItem : function(item)
28783     {
28784         var prev = false;
28785         
28786         Roo.each(this.barItems, function(v){
28787             if (v.rid == item.rid) {
28788                 return ;
28789             }
28790             
28791             if (v.isActive()) {
28792                 v.setActive(false);
28793                 prev = v;
28794             }
28795         });
28796
28797         item.setActive(true);
28798         
28799         this.fireEvent('changed', this, item, prev);
28800     },
28801     
28802     getBarItem: function(rid)
28803     {
28804         var ret = false;
28805         
28806         Roo.each(this.barItems, function(e) {
28807             if (e.rid != rid) {
28808                 return;
28809             }
28810             
28811             ret =  e;
28812             return false;
28813         });
28814         
28815         return ret;
28816     },
28817     
28818     indexOfItem : function(item)
28819     {
28820         var index = false;
28821         
28822         Roo.each(this.barItems, function(v, i){
28823             
28824             if (v.rid != item.rid) {
28825                 return;
28826             }
28827             
28828             index = i;
28829             return false
28830         });
28831         
28832         return index;
28833     },
28834     
28835     setActiveNext : function()
28836     {
28837         var i = this.indexOfItem(this.getActive());
28838         
28839         if (i > this.barItems.length) {
28840             return;
28841         }
28842         
28843         this.setActiveItem(this.barItems[i+1]);
28844     },
28845     
28846     setActivePrev : function()
28847     {
28848         var i = this.indexOfItem(this.getActive());
28849         
28850         if (i  < 1) {
28851             return;
28852         }
28853         
28854         this.setActiveItem(this.barItems[i-1]);
28855     },
28856     
28857     format : function()
28858     {
28859         if(!this.barItems.length){
28860             return;
28861         }
28862      
28863         var width = 100 / this.barItems.length;
28864         
28865         Roo.each(this.barItems, function(i){
28866             i.el.setStyle('width', width + '%');
28867             i.topEl.el.setStyle('width', width + '%');
28868             i.bottomEl.el.setStyle('width', width + '%');
28869         }, this);
28870         
28871     }
28872     
28873 });
28874 /*
28875  * - LGPL
28876  *
28877  * Nav Progress Item
28878  * 
28879  */
28880
28881 /**
28882  * @class Roo.bootstrap.NavProgressItem
28883  * @extends Roo.bootstrap.Component
28884  * Bootstrap NavProgressItem class
28885  * @cfg {String} rid the reference id
28886  * @cfg {Boolean} active (true|false) Is item active default false
28887  * @cfg {Boolean} disabled (true|false) Is item active default false
28888  * @cfg {String} html
28889  * @cfg {String} position (top|bottom) text position default bottom
28890  * @cfg {String} icon show icon instead of number
28891  * 
28892  * @constructor
28893  * Create a new NavProgressItem
28894  * @param {Object} config The config object
28895  */
28896 Roo.bootstrap.NavProgressItem = function(config){
28897     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28898     this.addEvents({
28899         // raw events
28900         /**
28901          * @event click
28902          * The raw click event for the entire grid.
28903          * @param {Roo.bootstrap.NavProgressItem} this
28904          * @param {Roo.EventObject} e
28905          */
28906         "click" : true
28907     });
28908    
28909 };
28910
28911 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
28912     
28913     rid : '',
28914     active : false,
28915     disabled : false,
28916     html : '',
28917     position : 'bottom',
28918     icon : false,
28919     
28920     getAutoCreate : function()
28921     {
28922         var iconCls = 'roo-navigation-bar-item-icon';
28923         
28924         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28925         
28926         var cfg = {
28927             tag: 'li',
28928             cls: 'roo-navigation-bar-item',
28929             cn : [
28930                 {
28931                     tag : 'i',
28932                     cls : iconCls
28933                 }
28934             ]
28935         };
28936         
28937         if(this.active){
28938             cfg.cls += ' active';
28939         }
28940         if(this.disabled){
28941             cfg.cls += ' disabled';
28942         }
28943         
28944         return cfg;
28945     },
28946     
28947     disable : function()
28948     {
28949         this.setDisabled(true);
28950     },
28951     
28952     enable : function()
28953     {
28954         this.setDisabled(false);
28955     },
28956     
28957     initEvents: function() 
28958     {
28959         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28960         
28961         this.iconEl.on('click', this.onClick, this);
28962     },
28963     
28964     onClick : function(e)
28965     {
28966         e.preventDefault();
28967         
28968         if(this.disabled){
28969             return;
28970         }
28971         
28972         if(this.fireEvent('click', this, e) === false){
28973             return;
28974         };
28975         
28976         this.parent().setActiveItem(this);
28977     },
28978     
28979     isActive: function () 
28980     {
28981         return this.active;
28982     },
28983     
28984     setActive : function(state)
28985     {
28986         if(this.active == state){
28987             return;
28988         }
28989         
28990         this.active = state;
28991         
28992         if (state) {
28993             this.el.addClass('active');
28994             return;
28995         }
28996         
28997         this.el.removeClass('active');
28998         
28999         return;
29000     },
29001     
29002     setDisabled : function(state)
29003     {
29004         if(this.disabled == state){
29005             return;
29006         }
29007         
29008         this.disabled = state;
29009         
29010         if (state) {
29011             this.el.addClass('disabled');
29012             return;
29013         }
29014         
29015         this.el.removeClass('disabled');
29016     },
29017     
29018     tooltipEl : function()
29019     {
29020         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29021     }
29022 });
29023  
29024
29025  /*
29026  * - LGPL
29027  *
29028  * FieldLabel
29029  * 
29030  */
29031
29032 /**
29033  * @class Roo.bootstrap.FieldLabel
29034  * @extends Roo.bootstrap.Component
29035  * Bootstrap FieldLabel class
29036  * @cfg {String} html contents of the element
29037  * @cfg {String} tag tag of the element default label
29038  * @cfg {String} cls class of the element
29039  * @cfg {String} target label target 
29040  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29041  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
29042  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
29043  * @cfg {String} iconTooltip default "This field is required"
29044  * 
29045  * @constructor
29046  * Create a new FieldLabel
29047  * @param {Object} config The config object
29048  */
29049
29050 Roo.bootstrap.FieldLabel = function(config){
29051     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29052     
29053     this.addEvents({
29054             /**
29055              * @event invalid
29056              * Fires after the field has been marked as invalid.
29057              * @param {Roo.form.FieldLabel} this
29058              * @param {String} msg The validation message
29059              */
29060             invalid : true,
29061             /**
29062              * @event valid
29063              * Fires after the field has been validated with no errors.
29064              * @param {Roo.form.FieldLabel} this
29065              */
29066             valid : true
29067         });
29068 };
29069
29070 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29071     
29072     tag: 'label',
29073     cls: '',
29074     html: '',
29075     target: '',
29076     allowBlank : true,
29077     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
29078     validClass : 'text-success fa fa-lg fa-check',
29079     iconTooltip : 'This field is required',
29080     
29081     getAutoCreate : function(){
29082         
29083         var cfg = {
29084             tag : this.tag,
29085             cls : 'roo-bootstrap-field-label ' + this.cls,
29086             for : this.target,
29087             cn : [
29088                 {
29089                     tag : 'i',
29090                     cls : '',
29091                     tooltip : this.iconTooltip
29092                 },
29093                 {
29094                     tag : 'span',
29095                     html : this.html
29096                 }
29097             ] 
29098         };
29099         
29100         return cfg;
29101     },
29102     
29103     initEvents: function() 
29104     {
29105         Roo.bootstrap.Element.superclass.initEvents.call(this);
29106         
29107         this.iconEl = this.el.select('i', true).first();
29108         
29109         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
29110         
29111         Roo.bootstrap.FieldLabel.register(this);
29112     },
29113     
29114     /**
29115      * Mark this field as valid
29116      */
29117     markValid : function()
29118     {
29119         this.iconEl.show();
29120         
29121         this.iconEl.removeClass(this.invalidClass);
29122         
29123         this.iconEl.addClass(this.validClass);
29124         
29125         this.fireEvent('valid', this);
29126     },
29127     
29128     /**
29129      * Mark this field as invalid
29130      * @param {String} msg The validation message
29131      */
29132     markInvalid : function(msg)
29133     {
29134         this.iconEl.show();
29135         
29136         this.iconEl.removeClass(this.validClass);
29137         
29138         this.iconEl.addClass(this.invalidClass);
29139         
29140         this.fireEvent('invalid', this, msg);
29141     }
29142     
29143    
29144 });
29145
29146 Roo.apply(Roo.bootstrap.FieldLabel, {
29147     
29148     groups: {},
29149     
29150      /**
29151     * register a FieldLabel Group
29152     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29153     */
29154     register : function(label)
29155     {
29156         if(this.groups.hasOwnProperty(label.target)){
29157             return;
29158         }
29159      
29160         this.groups[label.target] = label;
29161         
29162     },
29163     /**
29164     * fetch a FieldLabel Group based on the target
29165     * @param {string} target
29166     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29167     */
29168     get: function(target) {
29169         if (typeof(this.groups[target]) == 'undefined') {
29170             return false;
29171         }
29172         
29173         return this.groups[target] ;
29174     }
29175 });
29176
29177  
29178
29179  /*
29180  * - LGPL
29181  *
29182  * page DateSplitField.
29183  * 
29184  */
29185
29186
29187 /**
29188  * @class Roo.bootstrap.DateSplitField
29189  * @extends Roo.bootstrap.Component
29190  * Bootstrap DateSplitField class
29191  * @cfg {string} fieldLabel - the label associated
29192  * @cfg {Number} labelWidth set the width of label (0-12)
29193  * @cfg {String} labelAlign (top|left)
29194  * @cfg {Boolean} dayAllowBlank (true|false) default false
29195  * @cfg {Boolean} monthAllowBlank (true|false) default false
29196  * @cfg {Boolean} yearAllowBlank (true|false) default false
29197  * @cfg {string} dayPlaceholder 
29198  * @cfg {string} monthPlaceholder
29199  * @cfg {string} yearPlaceholder
29200  * @cfg {string} dayFormat default 'd'
29201  * @cfg {string} monthFormat default 'm'
29202  * @cfg {string} yearFormat default 'Y'
29203  * @cfg {Number} labellg set the width of label (1-12)
29204  * @cfg {Number} labelmd set the width of label (1-12)
29205  * @cfg {Number} labelsm set the width of label (1-12)
29206  * @cfg {Number} labelxs set the width of label (1-12)
29207
29208  *     
29209  * @constructor
29210  * Create a new DateSplitField
29211  * @param {Object} config The config object
29212  */
29213
29214 Roo.bootstrap.DateSplitField = function(config){
29215     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29216     
29217     this.addEvents({
29218         // raw events
29219          /**
29220          * @event years
29221          * getting the data of years
29222          * @param {Roo.bootstrap.DateSplitField} this
29223          * @param {Object} years
29224          */
29225         "years" : true,
29226         /**
29227          * @event days
29228          * getting the data of days
29229          * @param {Roo.bootstrap.DateSplitField} this
29230          * @param {Object} days
29231          */
29232         "days" : true,
29233         /**
29234          * @event invalid
29235          * Fires after the field has been marked as invalid.
29236          * @param {Roo.form.Field} this
29237          * @param {String} msg The validation message
29238          */
29239         invalid : true,
29240        /**
29241          * @event valid
29242          * Fires after the field has been validated with no errors.
29243          * @param {Roo.form.Field} this
29244          */
29245         valid : true
29246     });
29247 };
29248
29249 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29250     
29251     fieldLabel : '',
29252     labelAlign : 'top',
29253     labelWidth : 3,
29254     dayAllowBlank : false,
29255     monthAllowBlank : false,
29256     yearAllowBlank : false,
29257     dayPlaceholder : '',
29258     monthPlaceholder : '',
29259     yearPlaceholder : '',
29260     dayFormat : 'd',
29261     monthFormat : 'm',
29262     yearFormat : 'Y',
29263     isFormField : true,
29264     labellg : 0,
29265     labelmd : 0,
29266     labelsm : 0,
29267     labelxs : 0,
29268     
29269     getAutoCreate : function()
29270     {
29271         var cfg = {
29272             tag : 'div',
29273             cls : 'row roo-date-split-field-group',
29274             cn : [
29275                 {
29276                     tag : 'input',
29277                     type : 'hidden',
29278                     cls : 'form-hidden-field roo-date-split-field-group-value',
29279                     name : this.name
29280                 }
29281             ]
29282         };
29283         
29284         var labelCls = 'col-md-12';
29285         var contentCls = 'col-md-4';
29286         
29287         if(this.fieldLabel){
29288             
29289             var label = {
29290                 tag : 'div',
29291                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29292                 cn : [
29293                     {
29294                         tag : 'label',
29295                         html : this.fieldLabel
29296                     }
29297                 ]
29298             };
29299             
29300             if(this.labelAlign == 'left'){
29301             
29302                 if(this.labelWidth > 12){
29303                     label.style = "width: " + this.labelWidth + 'px';
29304                 }
29305
29306                 if(this.labelWidth < 13 && this.labelmd == 0){
29307                     this.labelmd = this.labelWidth;
29308                 }
29309
29310                 if(this.labellg > 0){
29311                     labelCls = ' col-lg-' + this.labellg;
29312                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
29313                 }
29314
29315                 if(this.labelmd > 0){
29316                     labelCls = ' col-md-' + this.labelmd;
29317                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
29318                 }
29319
29320                 if(this.labelsm > 0){
29321                     labelCls = ' col-sm-' + this.labelsm;
29322                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
29323                 }
29324
29325                 if(this.labelxs > 0){
29326                     labelCls = ' col-xs-' + this.labelxs;
29327                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
29328                 }
29329             }
29330             
29331             label.cls += ' ' + labelCls;
29332             
29333             cfg.cn.push(label);
29334         }
29335         
29336         Roo.each(['day', 'month', 'year'], function(t){
29337             cfg.cn.push({
29338                 tag : 'div',
29339                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
29340             });
29341         }, this);
29342         
29343         return cfg;
29344     },
29345     
29346     inputEl: function ()
29347     {
29348         return this.el.select('.roo-date-split-field-group-value', true).first();
29349     },
29350     
29351     onRender : function(ct, position) 
29352     {
29353         var _this = this;
29354         
29355         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29356         
29357         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29358         
29359         this.dayField = new Roo.bootstrap.ComboBox({
29360             allowBlank : this.dayAllowBlank,
29361             alwaysQuery : true,
29362             displayField : 'value',
29363             editable : false,
29364             fieldLabel : '',
29365             forceSelection : true,
29366             mode : 'local',
29367             placeholder : this.dayPlaceholder,
29368             selectOnFocus : true,
29369             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29370             triggerAction : 'all',
29371             typeAhead : true,
29372             valueField : 'value',
29373             store : new Roo.data.SimpleStore({
29374                 data : (function() {    
29375                     var days = [];
29376                     _this.fireEvent('days', _this, days);
29377                     return days;
29378                 })(),
29379                 fields : [ 'value' ]
29380             }),
29381             listeners : {
29382                 select : function (_self, record, index)
29383                 {
29384                     _this.setValue(_this.getValue());
29385                 }
29386             }
29387         });
29388
29389         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29390         
29391         this.monthField = new Roo.bootstrap.MonthField({
29392             after : '<i class=\"fa fa-calendar\"></i>',
29393             allowBlank : this.monthAllowBlank,
29394             placeholder : this.monthPlaceholder,
29395             readOnly : true,
29396             listeners : {
29397                 render : function (_self)
29398                 {
29399                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
29400                         e.preventDefault();
29401                         _self.focus();
29402                     });
29403                 },
29404                 select : function (_self, oldvalue, newvalue)
29405                 {
29406                     _this.setValue(_this.getValue());
29407                 }
29408             }
29409         });
29410         
29411         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29412         
29413         this.yearField = new Roo.bootstrap.ComboBox({
29414             allowBlank : this.yearAllowBlank,
29415             alwaysQuery : true,
29416             displayField : 'value',
29417             editable : false,
29418             fieldLabel : '',
29419             forceSelection : true,
29420             mode : 'local',
29421             placeholder : this.yearPlaceholder,
29422             selectOnFocus : true,
29423             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29424             triggerAction : 'all',
29425             typeAhead : true,
29426             valueField : 'value',
29427             store : new Roo.data.SimpleStore({
29428                 data : (function() {
29429                     var years = [];
29430                     _this.fireEvent('years', _this, years);
29431                     return years;
29432                 })(),
29433                 fields : [ 'value' ]
29434             }),
29435             listeners : {
29436                 select : function (_self, record, index)
29437                 {
29438                     _this.setValue(_this.getValue());
29439                 }
29440             }
29441         });
29442
29443         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29444     },
29445     
29446     setValue : function(v, format)
29447     {
29448         this.inputEl.dom.value = v;
29449         
29450         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29451         
29452         var d = Date.parseDate(v, f);
29453         
29454         if(!d){
29455             this.validate();
29456             return;
29457         }
29458         
29459         this.setDay(d.format(this.dayFormat));
29460         this.setMonth(d.format(this.monthFormat));
29461         this.setYear(d.format(this.yearFormat));
29462         
29463         this.validate();
29464         
29465         return;
29466     },
29467     
29468     setDay : function(v)
29469     {
29470         this.dayField.setValue(v);
29471         this.inputEl.dom.value = this.getValue();
29472         this.validate();
29473         return;
29474     },
29475     
29476     setMonth : function(v)
29477     {
29478         this.monthField.setValue(v, true);
29479         this.inputEl.dom.value = this.getValue();
29480         this.validate();
29481         return;
29482     },
29483     
29484     setYear : function(v)
29485     {
29486         this.yearField.setValue(v);
29487         this.inputEl.dom.value = this.getValue();
29488         this.validate();
29489         return;
29490     },
29491     
29492     getDay : function()
29493     {
29494         return this.dayField.getValue();
29495     },
29496     
29497     getMonth : function()
29498     {
29499         return this.monthField.getValue();
29500     },
29501     
29502     getYear : function()
29503     {
29504         return this.yearField.getValue();
29505     },
29506     
29507     getValue : function()
29508     {
29509         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
29510         
29511         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
29512         
29513         return date;
29514     },
29515     
29516     reset : function()
29517     {
29518         this.setDay('');
29519         this.setMonth('');
29520         this.setYear('');
29521         this.inputEl.dom.value = '';
29522         this.validate();
29523         return;
29524     },
29525     
29526     validate : function()
29527     {
29528         var d = this.dayField.validate();
29529         var m = this.monthField.validate();
29530         var y = this.yearField.validate();
29531         
29532         var valid = true;
29533         
29534         if(
29535                 (!this.dayAllowBlank && !d) ||
29536                 (!this.monthAllowBlank && !m) ||
29537                 (!this.yearAllowBlank && !y)
29538         ){
29539             valid = false;
29540         }
29541         
29542         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
29543             return valid;
29544         }
29545         
29546         if(valid){
29547             this.markValid();
29548             return valid;
29549         }
29550         
29551         this.markInvalid();
29552         
29553         return valid;
29554     },
29555     
29556     markValid : function()
29557     {
29558         
29559         var label = this.el.select('label', true).first();
29560         var icon = this.el.select('i.fa-star', true).first();
29561
29562         if(label && icon){
29563             icon.remove();
29564         }
29565         
29566         this.fireEvent('valid', this);
29567     },
29568     
29569      /**
29570      * Mark this field as invalid
29571      * @param {String} msg The validation message
29572      */
29573     markInvalid : function(msg)
29574     {
29575         
29576         var label = this.el.select('label', true).first();
29577         var icon = this.el.select('i.fa-star', true).first();
29578
29579         if(label && !icon){
29580             this.el.select('.roo-date-split-field-label', true).createChild({
29581                 tag : 'i',
29582                 cls : 'text-danger fa fa-lg fa-star',
29583                 tooltip : 'This field is required',
29584                 style : 'margin-right:5px;'
29585             }, label, true);
29586         }
29587         
29588         this.fireEvent('invalid', this, msg);
29589     },
29590     
29591     clearInvalid : function()
29592     {
29593         var label = this.el.select('label', true).first();
29594         var icon = this.el.select('i.fa-star', true).first();
29595
29596         if(label && icon){
29597             icon.remove();
29598         }
29599         
29600         this.fireEvent('valid', this);
29601     },
29602     
29603     getName: function()
29604     {
29605         return this.name;
29606     }
29607     
29608 });
29609
29610  /**
29611  *
29612  * This is based on 
29613  * http://masonry.desandro.com
29614  *
29615  * The idea is to render all the bricks based on vertical width...
29616  *
29617  * The original code extends 'outlayer' - we might need to use that....
29618  * 
29619  */
29620
29621
29622 /**
29623  * @class Roo.bootstrap.LayoutMasonry
29624  * @extends Roo.bootstrap.Component
29625  * Bootstrap Layout Masonry class
29626  * 
29627  * @constructor
29628  * Create a new Element
29629  * @param {Object} config The config object
29630  */
29631
29632 Roo.bootstrap.LayoutMasonry = function(config){
29633     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
29634     
29635     this.bricks = [];
29636     
29637 };
29638
29639 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
29640     
29641     /**
29642      * @cfg {Boolean} isLayoutInstant = no animation?
29643      */   
29644     isLayoutInstant : false, // needed?
29645    
29646     /**
29647      * @cfg {Number} boxWidth  width of the columns
29648      */   
29649     boxWidth : 450,
29650     
29651       /**
29652      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
29653      */   
29654     boxHeight : 0,
29655     
29656     /**
29657      * @cfg {Number} padWidth padding below box..
29658      */   
29659     padWidth : 10, 
29660     
29661     /**
29662      * @cfg {Number} gutter gutter width..
29663      */   
29664     gutter : 10,
29665     
29666      /**
29667      * @cfg {Number} maxCols maximum number of columns
29668      */   
29669     
29670     maxCols: 0,
29671     
29672     /**
29673      * @cfg {Boolean} isAutoInitial defalut true
29674      */   
29675     isAutoInitial : true, 
29676     
29677     containerWidth: 0,
29678     
29679     /**
29680      * @cfg {Boolean} isHorizontal defalut false
29681      */   
29682     isHorizontal : false, 
29683
29684     currentSize : null,
29685     
29686     tag: 'div',
29687     
29688     cls: '',
29689     
29690     bricks: null, //CompositeElement
29691     
29692     cols : 1,
29693     
29694     _isLayoutInited : false,
29695     
29696 //    isAlternative : false, // only use for vertical layout...
29697     
29698     /**
29699      * @cfg {Number} alternativePadWidth padding below box..
29700      */   
29701     alternativePadWidth : 50, 
29702     
29703     getAutoCreate : function(){
29704         
29705         var cfg = {
29706             tag: this.tag,
29707             cls: 'blog-masonary-wrapper ' + this.cls,
29708             cn : {
29709                 cls : 'mas-boxes masonary'
29710             }
29711         };
29712         
29713         return cfg;
29714     },
29715     
29716     getChildContainer: function( )
29717     {
29718         if (this.boxesEl) {
29719             return this.boxesEl;
29720         }
29721         
29722         this.boxesEl = this.el.select('.mas-boxes').first();
29723         
29724         return this.boxesEl;
29725     },
29726     
29727     
29728     initEvents : function()
29729     {
29730         var _this = this;
29731         
29732         if(this.isAutoInitial){
29733             Roo.log('hook children rendered');
29734             this.on('childrenrendered', function() {
29735                 Roo.log('children rendered');
29736                 _this.initial();
29737             } ,this);
29738         }
29739     },
29740     
29741     initial : function()
29742     {
29743         this.currentSize = this.el.getBox(true);
29744         
29745         Roo.EventManager.onWindowResize(this.resize, this); 
29746
29747         if(!this.isAutoInitial){
29748             this.layout();
29749             return;
29750         }
29751         
29752         this.layout();
29753         
29754         return;
29755         //this.layout.defer(500,this);
29756         
29757     },
29758     
29759     resize : function()
29760     {
29761         Roo.log('resize');
29762         
29763         var cs = this.el.getBox(true);
29764         
29765         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29766             Roo.log("no change in with or X");
29767             return;
29768         }
29769         
29770         this.currentSize = cs;
29771         
29772         this.layout();
29773         
29774     },
29775     
29776     layout : function()
29777     {   
29778         this._resetLayout();
29779         
29780         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29781         
29782         this.layoutItems( isInstant );
29783       
29784         this._isLayoutInited = true;
29785         
29786     },
29787     
29788     _resetLayout : function()
29789     {
29790         if(this.isHorizontal){
29791             this.horizontalMeasureColumns();
29792             return;
29793         }
29794         
29795         this.verticalMeasureColumns();
29796         
29797     },
29798     
29799     verticalMeasureColumns : function()
29800     {
29801         this.getContainerWidth();
29802         
29803 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29804 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
29805 //            return;
29806 //        }
29807         
29808         var boxWidth = this.boxWidth + this.padWidth;
29809         
29810         if(this.containerWidth < this.boxWidth){
29811             boxWidth = this.containerWidth
29812         }
29813         
29814         var containerWidth = this.containerWidth;
29815         
29816         var cols = Math.floor(containerWidth / boxWidth);
29817         
29818         this.cols = Math.max( cols, 1 );
29819         
29820         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29821         
29822         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29823         
29824         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29825         
29826         this.colWidth = boxWidth + avail - this.padWidth;
29827         
29828         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29829         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
29830     },
29831     
29832     horizontalMeasureColumns : function()
29833     {
29834         this.getContainerWidth();
29835         
29836         var boxWidth = this.boxWidth;
29837         
29838         if(this.containerWidth < boxWidth){
29839             boxWidth = this.containerWidth;
29840         }
29841         
29842         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29843         
29844         this.el.setHeight(boxWidth);
29845         
29846     },
29847     
29848     getContainerWidth : function()
29849     {
29850         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
29851     },
29852     
29853     layoutItems : function( isInstant )
29854     {
29855         var items = Roo.apply([], this.bricks);
29856         
29857         if(this.isHorizontal){
29858             this._horizontalLayoutItems( items , isInstant );
29859             return;
29860         }
29861         
29862 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29863 //            this._verticalAlternativeLayoutItems( items , isInstant );
29864 //            return;
29865 //        }
29866         
29867         this._verticalLayoutItems( items , isInstant );
29868         
29869     },
29870     
29871     _verticalLayoutItems : function ( items , isInstant)
29872     {
29873         if ( !items || !items.length ) {
29874             return;
29875         }
29876         
29877         var standard = [
29878             ['xs', 'xs', 'xs', 'tall'],
29879             ['xs', 'xs', 'tall'],
29880             ['xs', 'xs', 'sm'],
29881             ['xs', 'xs', 'xs'],
29882             ['xs', 'tall'],
29883             ['xs', 'sm'],
29884             ['xs', 'xs'],
29885             ['xs'],
29886             
29887             ['sm', 'xs', 'xs'],
29888             ['sm', 'xs'],
29889             ['sm'],
29890             
29891             ['tall', 'xs', 'xs', 'xs'],
29892             ['tall', 'xs', 'xs'],
29893             ['tall', 'xs'],
29894             ['tall']
29895             
29896         ];
29897         
29898         var queue = [];
29899         
29900         var boxes = [];
29901         
29902         var box = [];
29903         
29904         Roo.each(items, function(item, k){
29905             
29906             switch (item.size) {
29907                 // these layouts take up a full box,
29908                 case 'md' :
29909                 case 'md-left' :
29910                 case 'md-right' :
29911                 case 'wide' :
29912                     
29913                     if(box.length){
29914                         boxes.push(box);
29915                         box = [];
29916                     }
29917                     
29918                     boxes.push([item]);
29919                     
29920                     break;
29921                     
29922                 case 'xs' :
29923                 case 'sm' :
29924                 case 'tall' :
29925                     
29926                     box.push(item);
29927                     
29928                     break;
29929                 default :
29930                     break;
29931                     
29932             }
29933             
29934         }, this);
29935         
29936         if(box.length){
29937             boxes.push(box);
29938             box = [];
29939         }
29940         
29941         var filterPattern = function(box, length)
29942         {
29943             if(!box.length){
29944                 return;
29945             }
29946             
29947             var match = false;
29948             
29949             var pattern = box.slice(0, length);
29950             
29951             var format = [];
29952             
29953             Roo.each(pattern, function(i){
29954                 format.push(i.size);
29955             }, this);
29956             
29957             Roo.each(standard, function(s){
29958                 
29959                 if(String(s) != String(format)){
29960                     return;
29961                 }
29962                 
29963                 match = true;
29964                 return false;
29965                 
29966             }, this);
29967             
29968             if(!match && length == 1){
29969                 return;
29970             }
29971             
29972             if(!match){
29973                 filterPattern(box, length - 1);
29974                 return;
29975             }
29976                 
29977             queue.push(pattern);
29978
29979             box = box.slice(length, box.length);
29980
29981             filterPattern(box, 4);
29982
29983             return;
29984             
29985         }
29986         
29987         Roo.each(boxes, function(box, k){
29988             
29989             if(!box.length){
29990                 return;
29991             }
29992             
29993             if(box.length == 1){
29994                 queue.push(box);
29995                 return;
29996             }
29997             
29998             filterPattern(box, 4);
29999             
30000         }, this);
30001         
30002         this._processVerticalLayoutQueue( queue, isInstant );
30003         
30004     },
30005     
30006 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30007 //    {
30008 //        if ( !items || !items.length ) {
30009 //            return;
30010 //        }
30011 //
30012 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30013 //        
30014 //    },
30015     
30016     _horizontalLayoutItems : function ( items , isInstant)
30017     {
30018         if ( !items || !items.length || items.length < 3) {
30019             return;
30020         }
30021         
30022         items.reverse();
30023         
30024         var eItems = items.slice(0, 3);
30025         
30026         items = items.slice(3, items.length);
30027         
30028         var standard = [
30029             ['xs', 'xs', 'xs', 'wide'],
30030             ['xs', 'xs', 'wide'],
30031             ['xs', 'xs', 'sm'],
30032             ['xs', 'xs', 'xs'],
30033             ['xs', 'wide'],
30034             ['xs', 'sm'],
30035             ['xs', 'xs'],
30036             ['xs'],
30037             
30038             ['sm', 'xs', 'xs'],
30039             ['sm', 'xs'],
30040             ['sm'],
30041             
30042             ['wide', 'xs', 'xs', 'xs'],
30043             ['wide', 'xs', 'xs'],
30044             ['wide', 'xs'],
30045             ['wide'],
30046             
30047             ['wide-thin']
30048         ];
30049         
30050         var queue = [];
30051         
30052         var boxes = [];
30053         
30054         var box = [];
30055         
30056         Roo.each(items, function(item, k){
30057             
30058             switch (item.size) {
30059                 case 'md' :
30060                 case 'md-left' :
30061                 case 'md-right' :
30062                 case 'tall' :
30063                     
30064                     if(box.length){
30065                         boxes.push(box);
30066                         box = [];
30067                     }
30068                     
30069                     boxes.push([item]);
30070                     
30071                     break;
30072                     
30073                 case 'xs' :
30074                 case 'sm' :
30075                 case 'wide' :
30076                 case 'wide-thin' :
30077                     
30078                     box.push(item);
30079                     
30080                     break;
30081                 default :
30082                     break;
30083                     
30084             }
30085             
30086         }, this);
30087         
30088         if(box.length){
30089             boxes.push(box);
30090             box = [];
30091         }
30092         
30093         var filterPattern = function(box, length)
30094         {
30095             if(!box.length){
30096                 return;
30097             }
30098             
30099             var match = false;
30100             
30101             var pattern = box.slice(0, length);
30102             
30103             var format = [];
30104             
30105             Roo.each(pattern, function(i){
30106                 format.push(i.size);
30107             }, this);
30108             
30109             Roo.each(standard, function(s){
30110                 
30111                 if(String(s) != String(format)){
30112                     return;
30113                 }
30114                 
30115                 match = true;
30116                 return false;
30117                 
30118             }, this);
30119             
30120             if(!match && length == 1){
30121                 return;
30122             }
30123             
30124             if(!match){
30125                 filterPattern(box, length - 1);
30126                 return;
30127             }
30128                 
30129             queue.push(pattern);
30130
30131             box = box.slice(length, box.length);
30132
30133             filterPattern(box, 4);
30134
30135             return;
30136             
30137         }
30138         
30139         Roo.each(boxes, function(box, k){
30140             
30141             if(!box.length){
30142                 return;
30143             }
30144             
30145             if(box.length == 1){
30146                 queue.push(box);
30147                 return;
30148             }
30149             
30150             filterPattern(box, 4);
30151             
30152         }, this);
30153         
30154         
30155         var prune = [];
30156         
30157         var pos = this.el.getBox(true);
30158         
30159         var minX = pos.x;
30160         
30161         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30162         
30163         var hit_end = false;
30164         
30165         Roo.each(queue, function(box){
30166             
30167             if(hit_end){
30168                 
30169                 Roo.each(box, function(b){
30170                 
30171                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30172                     b.el.hide();
30173
30174                 }, this);
30175
30176                 return;
30177             }
30178             
30179             var mx = 0;
30180             
30181             Roo.each(box, function(b){
30182                 
30183                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30184                 b.el.show();
30185
30186                 mx = Math.max(mx, b.x);
30187                 
30188             }, this);
30189             
30190             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30191             
30192             if(maxX < minX){
30193                 
30194                 Roo.each(box, function(b){
30195                 
30196                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30197                     b.el.hide();
30198                     
30199                 }, this);
30200                 
30201                 hit_end = true;
30202                 
30203                 return;
30204             }
30205             
30206             prune.push(box);
30207             
30208         }, this);
30209         
30210         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30211     },
30212     
30213     /** Sets position of item in DOM
30214     * @param {Element} item
30215     * @param {Number} x - horizontal position
30216     * @param {Number} y - vertical position
30217     * @param {Boolean} isInstant - disables transitions
30218     */
30219     _processVerticalLayoutQueue : function( queue, isInstant )
30220     {
30221         var pos = this.el.getBox(true);
30222         var x = pos.x;
30223         var y = pos.y;
30224         var maxY = [];
30225         
30226         for (var i = 0; i < this.cols; i++){
30227             maxY[i] = pos.y;
30228         }
30229         
30230         Roo.each(queue, function(box, k){
30231             
30232             var col = k % this.cols;
30233             
30234             Roo.each(box, function(b,kk){
30235                 
30236                 b.el.position('absolute');
30237                 
30238                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30239                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30240                 
30241                 if(b.size == 'md-left' || b.size == 'md-right'){
30242                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30243                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30244                 }
30245                 
30246                 b.el.setWidth(width);
30247                 b.el.setHeight(height);
30248                 // iframe?
30249                 b.el.select('iframe',true).setSize(width,height);
30250                 
30251             }, this);
30252             
30253             for (var i = 0; i < this.cols; i++){
30254                 
30255                 if(maxY[i] < maxY[col]){
30256                     col = i;
30257                     continue;
30258                 }
30259                 
30260                 col = Math.min(col, i);
30261                 
30262             }
30263             
30264             x = pos.x + col * (this.colWidth + this.padWidth);
30265             
30266             y = maxY[col];
30267             
30268             var positions = [];
30269             
30270             switch (box.length){
30271                 case 1 :
30272                     positions = this.getVerticalOneBoxColPositions(x, y, box);
30273                     break;
30274                 case 2 :
30275                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
30276                     break;
30277                 case 3 :
30278                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
30279                     break;
30280                 case 4 :
30281                     positions = this.getVerticalFourBoxColPositions(x, y, box);
30282                     break;
30283                 default :
30284                     break;
30285             }
30286             
30287             Roo.each(box, function(b,kk){
30288                 
30289                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30290                 
30291                 var sz = b.el.getSize();
30292                 
30293                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
30294                 
30295             }, this);
30296             
30297         }, this);
30298         
30299         var mY = 0;
30300         
30301         for (var i = 0; i < this.cols; i++){
30302             mY = Math.max(mY, maxY[i]);
30303         }
30304         
30305         this.el.setHeight(mY - pos.y);
30306         
30307     },
30308     
30309 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
30310 //    {
30311 //        var pos = this.el.getBox(true);
30312 //        var x = pos.x;
30313 //        var y = pos.y;
30314 //        var maxX = pos.right;
30315 //        
30316 //        var maxHeight = 0;
30317 //        
30318 //        Roo.each(items, function(item, k){
30319 //            
30320 //            var c = k % 2;
30321 //            
30322 //            item.el.position('absolute');
30323 //                
30324 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30325 //
30326 //            item.el.setWidth(width);
30327 //
30328 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
30329 //
30330 //            item.el.setHeight(height);
30331 //            
30332 //            if(c == 0){
30333 //                item.el.setXY([x, y], isInstant ? false : true);
30334 //            } else {
30335 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
30336 //            }
30337 //            
30338 //            y = y + height + this.alternativePadWidth;
30339 //            
30340 //            maxHeight = maxHeight + height + this.alternativePadWidth;
30341 //            
30342 //        }, this);
30343 //        
30344 //        this.el.setHeight(maxHeight);
30345 //        
30346 //    },
30347     
30348     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30349     {
30350         var pos = this.el.getBox(true);
30351         
30352         var minX = pos.x;
30353         var minY = pos.y;
30354         
30355         var maxX = pos.right;
30356         
30357         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
30358         
30359         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30360         
30361         Roo.each(queue, function(box, k){
30362             
30363             Roo.each(box, function(b, kk){
30364                 
30365                 b.el.position('absolute');
30366                 
30367                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30368                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30369                 
30370                 if(b.size == 'md-left' || b.size == 'md-right'){
30371                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30372                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30373                 }
30374                 
30375                 b.el.setWidth(width);
30376                 b.el.setHeight(height);
30377                 
30378             }, this);
30379             
30380             if(!box.length){
30381                 return;
30382             }
30383             
30384             var positions = [];
30385             
30386             switch (box.length){
30387                 case 1 :
30388                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30389                     break;
30390                 case 2 :
30391                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30392                     break;
30393                 case 3 :
30394                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30395                     break;
30396                 case 4 :
30397                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30398                     break;
30399                 default :
30400                     break;
30401             }
30402             
30403             Roo.each(box, function(b,kk){
30404                 
30405                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30406                 
30407                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30408                 
30409             }, this);
30410             
30411         }, this);
30412         
30413     },
30414     
30415     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30416     {
30417         Roo.each(eItems, function(b,k){
30418             
30419             b.size = (k == 0) ? 'sm' : 'xs';
30420             b.x = (k == 0) ? 2 : 1;
30421             b.y = (k == 0) ? 2 : 1;
30422             
30423             b.el.position('absolute');
30424             
30425             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30426                 
30427             b.el.setWidth(width);
30428             
30429             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30430             
30431             b.el.setHeight(height);
30432             
30433         }, this);
30434
30435         var positions = [];
30436         
30437         positions.push({
30438             x : maxX - this.unitWidth * 2 - this.gutter,
30439             y : minY
30440         });
30441         
30442         positions.push({
30443             x : maxX - this.unitWidth,
30444             y : minY + (this.unitWidth + this.gutter) * 2
30445         });
30446         
30447         positions.push({
30448             x : maxX - this.unitWidth * 3 - this.gutter * 2,
30449             y : minY
30450         });
30451         
30452         Roo.each(eItems, function(b,k){
30453             
30454             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
30455
30456         }, this);
30457         
30458     },
30459     
30460     getVerticalOneBoxColPositions : function(x, y, box)
30461     {
30462         var pos = [];
30463         
30464         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
30465         
30466         if(box[0].size == 'md-left'){
30467             rand = 0;
30468         }
30469         
30470         if(box[0].size == 'md-right'){
30471             rand = 1;
30472         }
30473         
30474         pos.push({
30475             x : x + (this.unitWidth + this.gutter) * rand,
30476             y : y
30477         });
30478         
30479         return pos;
30480     },
30481     
30482     getVerticalTwoBoxColPositions : function(x, y, box)
30483     {
30484         var pos = [];
30485         
30486         if(box[0].size == 'xs'){
30487             
30488             pos.push({
30489                 x : x,
30490                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
30491             });
30492
30493             pos.push({
30494                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
30495                 y : y
30496             });
30497             
30498             return pos;
30499             
30500         }
30501         
30502         pos.push({
30503             x : x,
30504             y : y
30505         });
30506
30507         pos.push({
30508             x : x + (this.unitWidth + this.gutter) * 2,
30509             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
30510         });
30511         
30512         return pos;
30513         
30514     },
30515     
30516     getVerticalThreeBoxColPositions : function(x, y, box)
30517     {
30518         var pos = [];
30519         
30520         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30521             
30522             pos.push({
30523                 x : x,
30524                 y : y
30525             });
30526
30527             pos.push({
30528                 x : x + (this.unitWidth + this.gutter) * 1,
30529                 y : y
30530             });
30531             
30532             pos.push({
30533                 x : x + (this.unitWidth + this.gutter) * 2,
30534                 y : y
30535             });
30536             
30537             return pos;
30538             
30539         }
30540         
30541         if(box[0].size == 'xs' && box[1].size == 'xs'){
30542             
30543             pos.push({
30544                 x : x,
30545                 y : y
30546             });
30547
30548             pos.push({
30549                 x : x,
30550                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
30551             });
30552             
30553             pos.push({
30554                 x : x + (this.unitWidth + this.gutter) * 1,
30555                 y : y
30556             });
30557             
30558             return pos;
30559             
30560         }
30561         
30562         pos.push({
30563             x : x,
30564             y : y
30565         });
30566
30567         pos.push({
30568             x : x + (this.unitWidth + this.gutter) * 2,
30569             y : y
30570         });
30571
30572         pos.push({
30573             x : x + (this.unitWidth + this.gutter) * 2,
30574             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
30575         });
30576             
30577         return pos;
30578         
30579     },
30580     
30581     getVerticalFourBoxColPositions : function(x, y, box)
30582     {
30583         var pos = [];
30584         
30585         if(box[0].size == 'xs'){
30586             
30587             pos.push({
30588                 x : x,
30589                 y : y
30590             });
30591
30592             pos.push({
30593                 x : x,
30594                 y : y + (this.unitHeight + this.gutter) * 1
30595             });
30596             
30597             pos.push({
30598                 x : x,
30599                 y : y + (this.unitHeight + this.gutter) * 2
30600             });
30601             
30602             pos.push({
30603                 x : x + (this.unitWidth + this.gutter) * 1,
30604                 y : y
30605             });
30606             
30607             return pos;
30608             
30609         }
30610         
30611         pos.push({
30612             x : x,
30613             y : y
30614         });
30615
30616         pos.push({
30617             x : x + (this.unitWidth + this.gutter) * 2,
30618             y : y
30619         });
30620
30621         pos.push({
30622             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
30623             y : y + (this.unitHeight + this.gutter) * 1
30624         });
30625
30626         pos.push({
30627             x : x + (this.unitWidth + this.gutter) * 2,
30628             y : y + (this.unitWidth + this.gutter) * 2
30629         });
30630
30631         return pos;
30632         
30633     },
30634     
30635     getHorizontalOneBoxColPositions : function(maxX, minY, box)
30636     {
30637         var pos = [];
30638         
30639         if(box[0].size == 'md-left'){
30640             pos.push({
30641                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30642                 y : minY
30643             });
30644             
30645             return pos;
30646         }
30647         
30648         if(box[0].size == 'md-right'){
30649             pos.push({
30650                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30651                 y : minY + (this.unitWidth + this.gutter) * 1
30652             });
30653             
30654             return pos;
30655         }
30656         
30657         var rand = Math.floor(Math.random() * (4 - box[0].y));
30658         
30659         pos.push({
30660             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30661             y : minY + (this.unitWidth + this.gutter) * rand
30662         });
30663         
30664         return pos;
30665         
30666     },
30667     
30668     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
30669     {
30670         var pos = [];
30671         
30672         if(box[0].size == 'xs'){
30673             
30674             pos.push({
30675                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30676                 y : minY
30677             });
30678
30679             pos.push({
30680                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30681                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
30682             });
30683             
30684             return pos;
30685             
30686         }
30687         
30688         pos.push({
30689             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30690             y : minY
30691         });
30692
30693         pos.push({
30694             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30695             y : minY + (this.unitWidth + this.gutter) * 2
30696         });
30697         
30698         return pos;
30699         
30700     },
30701     
30702     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30703     {
30704         var pos = [];
30705         
30706         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30707             
30708             pos.push({
30709                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30710                 y : minY
30711             });
30712
30713             pos.push({
30714                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30715                 y : minY + (this.unitWidth + this.gutter) * 1
30716             });
30717             
30718             pos.push({
30719                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30720                 y : minY + (this.unitWidth + this.gutter) * 2
30721             });
30722             
30723             return pos;
30724             
30725         }
30726         
30727         if(box[0].size == 'xs' && box[1].size == 'xs'){
30728             
30729             pos.push({
30730                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30731                 y : minY
30732             });
30733
30734             pos.push({
30735                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30736                 y : minY
30737             });
30738             
30739             pos.push({
30740                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30741                 y : minY + (this.unitWidth + this.gutter) * 1
30742             });
30743             
30744             return pos;
30745             
30746         }
30747         
30748         pos.push({
30749             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30750             y : minY
30751         });
30752
30753         pos.push({
30754             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30755             y : minY + (this.unitWidth + this.gutter) * 2
30756         });
30757
30758         pos.push({
30759             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30760             y : minY + (this.unitWidth + this.gutter) * 2
30761         });
30762             
30763         return pos;
30764         
30765     },
30766     
30767     getHorizontalFourBoxColPositions : function(maxX, minY, box)
30768     {
30769         var pos = [];
30770         
30771         if(box[0].size == 'xs'){
30772             
30773             pos.push({
30774                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30775                 y : minY
30776             });
30777
30778             pos.push({
30779                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30780                 y : minY
30781             });
30782             
30783             pos.push({
30784                 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),
30785                 y : minY
30786             });
30787             
30788             pos.push({
30789                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30790                 y : minY + (this.unitWidth + this.gutter) * 1
30791             });
30792             
30793             return pos;
30794             
30795         }
30796         
30797         pos.push({
30798             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30799             y : minY
30800         });
30801         
30802         pos.push({
30803             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30804             y : minY + (this.unitWidth + this.gutter) * 2
30805         });
30806         
30807         pos.push({
30808             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30809             y : minY + (this.unitWidth + this.gutter) * 2
30810         });
30811         
30812         pos.push({
30813             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),
30814             y : minY + (this.unitWidth + this.gutter) * 2
30815         });
30816
30817         return pos;
30818         
30819     }
30820     
30821 });
30822
30823  
30824
30825  /**
30826  *
30827  * This is based on 
30828  * http://masonry.desandro.com
30829  *
30830  * The idea is to render all the bricks based on vertical width...
30831  *
30832  * The original code extends 'outlayer' - we might need to use that....
30833  * 
30834  */
30835
30836
30837 /**
30838  * @class Roo.bootstrap.LayoutMasonryAuto
30839  * @extends Roo.bootstrap.Component
30840  * Bootstrap Layout Masonry class
30841  * 
30842  * @constructor
30843  * Create a new Element
30844  * @param {Object} config The config object
30845  */
30846
30847 Roo.bootstrap.LayoutMasonryAuto = function(config){
30848     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30849 };
30850
30851 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
30852     
30853       /**
30854      * @cfg {Boolean} isFitWidth  - resize the width..
30855      */   
30856     isFitWidth : false,  // options..
30857     /**
30858      * @cfg {Boolean} isOriginLeft = left align?
30859      */   
30860     isOriginLeft : true,
30861     /**
30862      * @cfg {Boolean} isOriginTop = top align?
30863      */   
30864     isOriginTop : false,
30865     /**
30866      * @cfg {Boolean} isLayoutInstant = no animation?
30867      */   
30868     isLayoutInstant : false, // needed?
30869     /**
30870      * @cfg {Boolean} isResizingContainer = not sure if this is used..
30871      */   
30872     isResizingContainer : true,
30873     /**
30874      * @cfg {Number} columnWidth  width of the columns 
30875      */   
30876     
30877     columnWidth : 0,
30878     
30879     /**
30880      * @cfg {Number} maxCols maximum number of columns
30881      */   
30882     
30883     maxCols: 0,
30884     /**
30885      * @cfg {Number} padHeight padding below box..
30886      */   
30887     
30888     padHeight : 10, 
30889     
30890     /**
30891      * @cfg {Boolean} isAutoInitial defalut true
30892      */   
30893     
30894     isAutoInitial : true, 
30895     
30896     // private?
30897     gutter : 0,
30898     
30899     containerWidth: 0,
30900     initialColumnWidth : 0,
30901     currentSize : null,
30902     
30903     colYs : null, // array.
30904     maxY : 0,
30905     padWidth: 10,
30906     
30907     
30908     tag: 'div',
30909     cls: '',
30910     bricks: null, //CompositeElement
30911     cols : 0, // array?
30912     // element : null, // wrapped now this.el
30913     _isLayoutInited : null, 
30914     
30915     
30916     getAutoCreate : function(){
30917         
30918         var cfg = {
30919             tag: this.tag,
30920             cls: 'blog-masonary-wrapper ' + this.cls,
30921             cn : {
30922                 cls : 'mas-boxes masonary'
30923             }
30924         };
30925         
30926         return cfg;
30927     },
30928     
30929     getChildContainer: function( )
30930     {
30931         if (this.boxesEl) {
30932             return this.boxesEl;
30933         }
30934         
30935         this.boxesEl = this.el.select('.mas-boxes').first();
30936         
30937         return this.boxesEl;
30938     },
30939     
30940     
30941     initEvents : function()
30942     {
30943         var _this = this;
30944         
30945         if(this.isAutoInitial){
30946             Roo.log('hook children rendered');
30947             this.on('childrenrendered', function() {
30948                 Roo.log('children rendered');
30949                 _this.initial();
30950             } ,this);
30951         }
30952         
30953     },
30954     
30955     initial : function()
30956     {
30957         this.reloadItems();
30958
30959         this.currentSize = this.el.getBox(true);
30960
30961         /// was window resize... - let's see if this works..
30962         Roo.EventManager.onWindowResize(this.resize, this); 
30963
30964         if(!this.isAutoInitial){
30965             this.layout();
30966             return;
30967         }
30968         
30969         this.layout.defer(500,this);
30970     },
30971     
30972     reloadItems: function()
30973     {
30974         this.bricks = this.el.select('.masonry-brick', true);
30975         
30976         this.bricks.each(function(b) {
30977             //Roo.log(b.getSize());
30978             if (!b.attr('originalwidth')) {
30979                 b.attr('originalwidth',  b.getSize().width);
30980             }
30981             
30982         });
30983         
30984         Roo.log(this.bricks.elements.length);
30985     },
30986     
30987     resize : function()
30988     {
30989         Roo.log('resize');
30990         var cs = this.el.getBox(true);
30991         
30992         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30993             Roo.log("no change in with or X");
30994             return;
30995         }
30996         this.currentSize = cs;
30997         this.layout();
30998     },
30999     
31000     layout : function()
31001     {
31002          Roo.log('layout');
31003         this._resetLayout();
31004         //this._manageStamps();
31005       
31006         // don't animate first layout
31007         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31008         this.layoutItems( isInstant );
31009       
31010         // flag for initalized
31011         this._isLayoutInited = true;
31012     },
31013     
31014     layoutItems : function( isInstant )
31015     {
31016         //var items = this._getItemsForLayout( this.items );
31017         // original code supports filtering layout items.. we just ignore it..
31018         
31019         this._layoutItems( this.bricks , isInstant );
31020       
31021         this._postLayout();
31022     },
31023     _layoutItems : function ( items , isInstant)
31024     {
31025        //this.fireEvent( 'layout', this, items );
31026     
31027
31028         if ( !items || !items.elements.length ) {
31029           // no items, emit event with empty array
31030             return;
31031         }
31032
31033         var queue = [];
31034         items.each(function(item) {
31035             Roo.log("layout item");
31036             Roo.log(item);
31037             // get x/y object from method
31038             var position = this._getItemLayoutPosition( item );
31039             // enqueue
31040             position.item = item;
31041             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31042             queue.push( position );
31043         }, this);
31044       
31045         this._processLayoutQueue( queue );
31046     },
31047     /** Sets position of item in DOM
31048     * @param {Element} item
31049     * @param {Number} x - horizontal position
31050     * @param {Number} y - vertical position
31051     * @param {Boolean} isInstant - disables transitions
31052     */
31053     _processLayoutQueue : function( queue )
31054     {
31055         for ( var i=0, len = queue.length; i < len; i++ ) {
31056             var obj = queue[i];
31057             obj.item.position('absolute');
31058             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31059         }
31060     },
31061       
31062     
31063     /**
31064     * Any logic you want to do after each layout,
31065     * i.e. size the container
31066     */
31067     _postLayout : function()
31068     {
31069         this.resizeContainer();
31070     },
31071     
31072     resizeContainer : function()
31073     {
31074         if ( !this.isResizingContainer ) {
31075             return;
31076         }
31077         var size = this._getContainerSize();
31078         if ( size ) {
31079             this.el.setSize(size.width,size.height);
31080             this.boxesEl.setSize(size.width,size.height);
31081         }
31082     },
31083     
31084     
31085     
31086     _resetLayout : function()
31087     {
31088         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31089         this.colWidth = this.el.getWidth();
31090         //this.gutter = this.el.getWidth(); 
31091         
31092         this.measureColumns();
31093
31094         // reset column Y
31095         var i = this.cols;
31096         this.colYs = [];
31097         while (i--) {
31098             this.colYs.push( 0 );
31099         }
31100     
31101         this.maxY = 0;
31102     },
31103
31104     measureColumns : function()
31105     {
31106         this.getContainerWidth();
31107       // if columnWidth is 0, default to outerWidth of first item
31108         if ( !this.columnWidth ) {
31109             var firstItem = this.bricks.first();
31110             Roo.log(firstItem);
31111             this.columnWidth  = this.containerWidth;
31112             if (firstItem && firstItem.attr('originalwidth') ) {
31113                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31114             }
31115             // columnWidth fall back to item of first element
31116             Roo.log("set column width?");
31117                         this.initialColumnWidth = this.columnWidth  ;
31118
31119             // if first elem has no width, default to size of container
31120             
31121         }
31122         
31123         
31124         if (this.initialColumnWidth) {
31125             this.columnWidth = this.initialColumnWidth;
31126         }
31127         
31128         
31129             
31130         // column width is fixed at the top - however if container width get's smaller we should
31131         // reduce it...
31132         
31133         // this bit calcs how man columns..
31134             
31135         var columnWidth = this.columnWidth += this.gutter;
31136       
31137         // calculate columns
31138         var containerWidth = this.containerWidth + this.gutter;
31139         
31140         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31141         // fix rounding errors, typically with gutters
31142         var excess = columnWidth - containerWidth % columnWidth;
31143         
31144         
31145         // if overshoot is less than a pixel, round up, otherwise floor it
31146         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31147         cols = Math[ mathMethod ]( cols );
31148         this.cols = Math.max( cols, 1 );
31149         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31150         
31151          // padding positioning..
31152         var totalColWidth = this.cols * this.columnWidth;
31153         var padavail = this.containerWidth - totalColWidth;
31154         // so for 2 columns - we need 3 'pads'
31155         
31156         var padNeeded = (1+this.cols) * this.padWidth;
31157         
31158         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
31159         
31160         this.columnWidth += padExtra
31161         //this.padWidth = Math.floor(padavail /  ( this.cols));
31162         
31163         // adjust colum width so that padding is fixed??
31164         
31165         // we have 3 columns ... total = width * 3
31166         // we have X left over... that should be used by 
31167         
31168         //if (this.expandC) {
31169             
31170         //}
31171         
31172         
31173         
31174     },
31175     
31176     getContainerWidth : function()
31177     {
31178        /* // container is parent if fit width
31179         var container = this.isFitWidth ? this.element.parentNode : this.element;
31180         // check that this.size and size are there
31181         // IE8 triggers resize on body size change, so they might not be
31182         
31183         var size = getSize( container );  //FIXME
31184         this.containerWidth = size && size.innerWidth; //FIXME
31185         */
31186          
31187         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31188         
31189     },
31190     
31191     _getItemLayoutPosition : function( item )  // what is item?
31192     {
31193         // we resize the item to our columnWidth..
31194       
31195         item.setWidth(this.columnWidth);
31196         item.autoBoxAdjust  = false;
31197         
31198         var sz = item.getSize();
31199  
31200         // how many columns does this brick span
31201         var remainder = this.containerWidth % this.columnWidth;
31202         
31203         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
31204         // round if off by 1 pixel, otherwise use ceil
31205         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
31206         colSpan = Math.min( colSpan, this.cols );
31207         
31208         // normally this should be '1' as we dont' currently allow multi width columns..
31209         
31210         var colGroup = this._getColGroup( colSpan );
31211         // get the minimum Y value from the columns
31212         var minimumY = Math.min.apply( Math, colGroup );
31213         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31214         
31215         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
31216          
31217         // position the brick
31218         var position = {
31219             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
31220             y: this.currentSize.y + minimumY + this.padHeight
31221         };
31222         
31223         Roo.log(position);
31224         // apply setHeight to necessary columns
31225         var setHeight = minimumY + sz.height + this.padHeight;
31226         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31227         
31228         var setSpan = this.cols + 1 - colGroup.length;
31229         for ( var i = 0; i < setSpan; i++ ) {
31230           this.colYs[ shortColIndex + i ] = setHeight ;
31231         }
31232       
31233         return position;
31234     },
31235     
31236     /**
31237      * @param {Number} colSpan - number of columns the element spans
31238      * @returns {Array} colGroup
31239      */
31240     _getColGroup : function( colSpan )
31241     {
31242         if ( colSpan < 2 ) {
31243           // if brick spans only one column, use all the column Ys
31244           return this.colYs;
31245         }
31246       
31247         var colGroup = [];
31248         // how many different places could this brick fit horizontally
31249         var groupCount = this.cols + 1 - colSpan;
31250         // for each group potential horizontal position
31251         for ( var i = 0; i < groupCount; i++ ) {
31252           // make an array of colY values for that one group
31253           var groupColYs = this.colYs.slice( i, i + colSpan );
31254           // and get the max value of the array
31255           colGroup[i] = Math.max.apply( Math, groupColYs );
31256         }
31257         return colGroup;
31258     },
31259     /*
31260     _manageStamp : function( stamp )
31261     {
31262         var stampSize =  stamp.getSize();
31263         var offset = stamp.getBox();
31264         // get the columns that this stamp affects
31265         var firstX = this.isOriginLeft ? offset.x : offset.right;
31266         var lastX = firstX + stampSize.width;
31267         var firstCol = Math.floor( firstX / this.columnWidth );
31268         firstCol = Math.max( 0, firstCol );
31269         
31270         var lastCol = Math.floor( lastX / this.columnWidth );
31271         // lastCol should not go over if multiple of columnWidth #425
31272         lastCol -= lastX % this.columnWidth ? 0 : 1;
31273         lastCol = Math.min( this.cols - 1, lastCol );
31274         
31275         // set colYs to bottom of the stamp
31276         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
31277             stampSize.height;
31278             
31279         for ( var i = firstCol; i <= lastCol; i++ ) {
31280           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
31281         }
31282     },
31283     */
31284     
31285     _getContainerSize : function()
31286     {
31287         this.maxY = Math.max.apply( Math, this.colYs );
31288         var size = {
31289             height: this.maxY
31290         };
31291       
31292         if ( this.isFitWidth ) {
31293             size.width = this._getContainerFitWidth();
31294         }
31295       
31296         return size;
31297     },
31298     
31299     _getContainerFitWidth : function()
31300     {
31301         var unusedCols = 0;
31302         // count unused columns
31303         var i = this.cols;
31304         while ( --i ) {
31305           if ( this.colYs[i] !== 0 ) {
31306             break;
31307           }
31308           unusedCols++;
31309         }
31310         // fit container to columns that have been used
31311         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
31312     },
31313     
31314     needsResizeLayout : function()
31315     {
31316         var previousWidth = this.containerWidth;
31317         this.getContainerWidth();
31318         return previousWidth !== this.containerWidth;
31319     }
31320  
31321 });
31322
31323  
31324
31325  /*
31326  * - LGPL
31327  *
31328  * element
31329  * 
31330  */
31331
31332 /**
31333  * @class Roo.bootstrap.MasonryBrick
31334  * @extends Roo.bootstrap.Component
31335  * Bootstrap MasonryBrick class
31336  * 
31337  * @constructor
31338  * Create a new MasonryBrick
31339  * @param {Object} config The config object
31340  */
31341
31342 Roo.bootstrap.MasonryBrick = function(config){
31343     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
31344     
31345     this.addEvents({
31346         // raw events
31347         /**
31348          * @event click
31349          * When a MasonryBrick is clcik
31350          * @param {Roo.bootstrap.MasonryBrick} this
31351          * @param {Roo.EventObject} e
31352          */
31353         "click" : true
31354     });
31355 };
31356
31357 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
31358     
31359     /**
31360      * @cfg {String} title
31361      */   
31362     title : '',
31363     /**
31364      * @cfg {String} html
31365      */   
31366     html : '',
31367     /**
31368      * @cfg {String} bgimage
31369      */   
31370     bgimage : '',
31371     /**
31372      * @cfg {String} videourl
31373      */   
31374     videourl : '',
31375     /**
31376      * @cfg {String} cls
31377      */   
31378     cls : '',
31379     /**
31380      * @cfg {String} href
31381      */   
31382     href : '',
31383     /**
31384      * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
31385      */   
31386     size : 'xs',
31387     
31388     /**
31389      * @cfg {String} (center|bottom) placetitle
31390      */   
31391     placetitle : '',
31392     
31393     /**
31394      * @cfg {Boolean} isFitContainer defalut true
31395      */   
31396     isFitContainer : true, 
31397     
31398     /**
31399      * @cfg {Boolean} preventDefault defalut false
31400      */   
31401     preventDefault : false, 
31402     
31403     getAutoCreate : function()
31404     {
31405         if(!this.isFitContainer){
31406             return this.getSplitAutoCreate();
31407         }
31408         
31409         var cls = 'masonry-brick masonry-brick-full';
31410         
31411         if(this.href.length){
31412             cls += ' masonry-brick-link';
31413         }
31414         
31415         if(this.bgimage.length){
31416             cls += ' masonry-brick-image';
31417         }
31418         
31419         if(!this.html.length){
31420             cls += ' enable-mask';
31421         }
31422         
31423         if(this.size){
31424             cls += ' masonry-' + this.size + '-brick';
31425         }
31426         
31427         if(this.placetitle.length){
31428             
31429             switch (this.placetitle) {
31430                 case 'center' :
31431                     cls += ' masonry-center-title';
31432                     break;
31433                 case 'bottom' :
31434                     cls += ' masonry-bottom-title';
31435                     break;
31436                 default:
31437                     break;
31438             }
31439             
31440         } else {
31441             if(!this.html.length && !this.bgimage.length){
31442                 cls += ' masonry-center-title';
31443             }
31444
31445             if(!this.html.length && this.bgimage.length){
31446                 cls += ' masonry-bottom-title';
31447             }
31448         }
31449         
31450         if(this.cls){
31451             cls += ' ' + this.cls;
31452         }
31453         
31454         var cfg = {
31455             tag: (this.href.length) ? 'a' : 'div',
31456             cls: cls,
31457             cn: [
31458                 {
31459                     tag: 'div',
31460                     cls: 'masonry-brick-paragraph',
31461                     cn: []
31462                 }
31463             ]
31464         };
31465         
31466         if(this.href.length){
31467             cfg.href = this.href;
31468         }
31469         
31470         var cn = cfg.cn[0].cn;
31471         
31472         if(this.title.length){
31473             cn.push({
31474                 tag: 'h4',
31475                 cls: 'masonry-brick-title',
31476                 html: this.title
31477             });
31478         }
31479         
31480         if(this.html.length){
31481             cn.push({
31482                 tag: 'p',
31483                 cls: 'masonry-brick-text',
31484                 html: this.html
31485             });
31486         }  
31487         if (!this.title.length && !this.html.length) {
31488             cfg.cn[0].cls += ' hide';
31489         }
31490         
31491         if(this.bgimage.length){
31492             cfg.cn.push({
31493                 tag: 'img',
31494                 cls: 'masonry-brick-image-view',
31495                 src: this.bgimage
31496             });
31497         }
31498         
31499         if(this.videourl.length){
31500             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31501             // youtube support only?
31502             cfg.cn.push({
31503                 tag: 'iframe',
31504                 cls: 'masonry-brick-image-view',
31505                 src: vurl,
31506                 frameborder : 0,
31507                 allowfullscreen : true
31508             });
31509             
31510             
31511         }
31512         
31513         cfg.cn.push({
31514             tag: 'div',
31515             cls: 'masonry-brick-mask'
31516         });
31517         
31518         return cfg;
31519         
31520     },
31521     
31522     getSplitAutoCreate : function()
31523     {
31524         var cls = 'masonry-brick masonry-brick-split';
31525         
31526         if(this.href.length){
31527             cls += ' masonry-brick-link';
31528         }
31529         
31530         if(this.bgimage.length){
31531             cls += ' masonry-brick-image';
31532         }
31533         
31534         if(this.size){
31535             cls += ' masonry-' + this.size + '-brick';
31536         }
31537         
31538         switch (this.placetitle) {
31539             case 'center' :
31540                 cls += ' masonry-center-title';
31541                 break;
31542             case 'bottom' :
31543                 cls += ' masonry-bottom-title';
31544                 break;
31545             default:
31546                 if(!this.bgimage.length){
31547                     cls += ' masonry-center-title';
31548                 }
31549
31550                 if(this.bgimage.length){
31551                     cls += ' masonry-bottom-title';
31552                 }
31553                 break;
31554         }
31555         
31556         if(this.cls){
31557             cls += ' ' + this.cls;
31558         }
31559         
31560         var cfg = {
31561             tag: (this.href.length) ? 'a' : 'div',
31562             cls: cls,
31563             cn: [
31564                 {
31565                     tag: 'div',
31566                     cls: 'masonry-brick-split-head',
31567                     cn: [
31568                         {
31569                             tag: 'div',
31570                             cls: 'masonry-brick-paragraph',
31571                             cn: []
31572                         }
31573                     ]
31574                 },
31575                 {
31576                     tag: 'div',
31577                     cls: 'masonry-brick-split-body',
31578                     cn: []
31579                 }
31580             ]
31581         };
31582         
31583         if(this.href.length){
31584             cfg.href = this.href;
31585         }
31586         
31587         if(this.title.length){
31588             cfg.cn[0].cn[0].cn.push({
31589                 tag: 'h4',
31590                 cls: 'masonry-brick-title',
31591                 html: this.title
31592             });
31593         }
31594         
31595         if(this.html.length){
31596             cfg.cn[1].cn.push({
31597                 tag: 'p',
31598                 cls: 'masonry-brick-text',
31599                 html: this.html
31600             });
31601         }
31602
31603         if(this.bgimage.length){
31604             cfg.cn[0].cn.push({
31605                 tag: 'img',
31606                 cls: 'masonry-brick-image-view',
31607                 src: this.bgimage
31608             });
31609         }
31610         
31611         if(this.videourl.length){
31612             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31613             // youtube support only?
31614             cfg.cn[0].cn.cn.push({
31615                 tag: 'iframe',
31616                 cls: 'masonry-brick-image-view',
31617                 src: vurl,
31618                 frameborder : 0,
31619                 allowfullscreen : true
31620             });
31621         }
31622         
31623         return cfg;
31624     },
31625     
31626     initEvents: function() 
31627     {
31628         switch (this.size) {
31629             case 'xs' :
31630                 this.x = 1;
31631                 this.y = 1;
31632                 break;
31633             case 'sm' :
31634                 this.x = 2;
31635                 this.y = 2;
31636                 break;
31637             case 'md' :
31638             case 'md-left' :
31639             case 'md-right' :
31640                 this.x = 3;
31641                 this.y = 3;
31642                 break;
31643             case 'tall' :
31644                 this.x = 2;
31645                 this.y = 3;
31646                 break;
31647             case 'wide' :
31648                 this.x = 3;
31649                 this.y = 2;
31650                 break;
31651             case 'wide-thin' :
31652                 this.x = 3;
31653                 this.y = 1;
31654                 break;
31655                         
31656             default :
31657                 break;
31658         }
31659         
31660         if(Roo.isTouch){
31661             this.el.on('touchstart', this.onTouchStart, this);
31662             this.el.on('touchmove', this.onTouchMove, this);
31663             this.el.on('touchend', this.onTouchEnd, this);
31664             this.el.on('contextmenu', this.onContextMenu, this);
31665         } else {
31666             this.el.on('mouseenter'  ,this.enter, this);
31667             this.el.on('mouseleave', this.leave, this);
31668             this.el.on('click', this.onClick, this);
31669         }
31670         
31671         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
31672             this.parent().bricks.push(this);   
31673         }
31674         
31675     },
31676     
31677     onClick: function(e, el)
31678     {
31679         var time = this.endTimer - this.startTimer;
31680         
31681         if(Roo.isTouch){
31682             if(time > 1000){
31683                 e.preventDefault();
31684                 return;
31685             }
31686         }
31687         
31688         if(!this.preventDefault){
31689             return;
31690         }
31691         
31692         e.preventDefault();
31693         this.fireEvent('click', this);
31694     },
31695     
31696     enter: function(e, el)
31697     {
31698         e.preventDefault();
31699         
31700         if(!this.isFitContainer){
31701             return;
31702         }
31703         
31704         if(this.bgimage.length && this.html.length){
31705             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31706         }
31707     },
31708     
31709     leave: function(e, el)
31710     {
31711         e.preventDefault();
31712         
31713         if(!this.isFitContainer){
31714             return;
31715         }
31716         
31717         if(this.bgimage.length && this.html.length){
31718             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31719         }
31720     },
31721     
31722     onTouchStart: function(e, el)
31723     {
31724 //        e.preventDefault();
31725         
31726         this.touchmoved = false;
31727         
31728         if(!this.isFitContainer){
31729             return;
31730         }
31731         
31732         if(!this.bgimage.length || !this.html.length){
31733             return;
31734         }
31735         
31736         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31737         
31738         this.timer = new Date().getTime();
31739         
31740     },
31741     
31742     onTouchMove: function(e, el)
31743     {
31744         this.touchmoved = true;
31745     },
31746     
31747     onContextMenu : function(e,el)
31748     {
31749         e.preventDefault();
31750         e.stopPropagation();
31751         return false;
31752     },
31753     
31754     onTouchEnd: function(e, el)
31755     {
31756 //        e.preventDefault();
31757         
31758         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
31759         
31760             this.leave(e,el);
31761             
31762             return;
31763         }
31764         
31765         if(!this.bgimage.length || !this.html.length){
31766             
31767             if(this.href.length){
31768                 window.location.href = this.href;
31769             }
31770             
31771             return;
31772         }
31773         
31774         if(!this.isFitContainer){
31775             return;
31776         }
31777         
31778         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31779         
31780         window.location.href = this.href;
31781     }
31782     
31783 });
31784
31785  
31786
31787  /*
31788  * - LGPL
31789  *
31790  * element
31791  * 
31792  */
31793
31794 /**
31795  * @class Roo.bootstrap.Brick
31796  * @extends Roo.bootstrap.Component
31797  * Bootstrap Brick class
31798  * 
31799  * @constructor
31800  * Create a new Brick
31801  * @param {Object} config The config object
31802  */
31803
31804 Roo.bootstrap.Brick = function(config){
31805     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
31806     
31807     this.addEvents({
31808         // raw events
31809         /**
31810          * @event click
31811          * When a Brick is click
31812          * @param {Roo.bootstrap.Brick} this
31813          * @param {Roo.EventObject} e
31814          */
31815         "click" : true
31816     });
31817 };
31818
31819 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
31820     
31821     /**
31822      * @cfg {String} title
31823      */   
31824     title : '',
31825     /**
31826      * @cfg {String} html
31827      */   
31828     html : '',
31829     /**
31830      * @cfg {String} bgimage
31831      */   
31832     bgimage : '',
31833     /**
31834      * @cfg {String} cls
31835      */   
31836     cls : '',
31837     /**
31838      * @cfg {String} href
31839      */   
31840     href : '',
31841     /**
31842      * @cfg {String} video
31843      */   
31844     video : '',
31845     /**
31846      * @cfg {Boolean} square
31847      */   
31848     square : true,
31849     
31850     getAutoCreate : function()
31851     {
31852         var cls = 'roo-brick';
31853         
31854         if(this.href.length){
31855             cls += ' roo-brick-link';
31856         }
31857         
31858         if(this.bgimage.length){
31859             cls += ' roo-brick-image';
31860         }
31861         
31862         if(!this.html.length && !this.bgimage.length){
31863             cls += ' roo-brick-center-title';
31864         }
31865         
31866         if(!this.html.length && this.bgimage.length){
31867             cls += ' roo-brick-bottom-title';
31868         }
31869         
31870         if(this.cls){
31871             cls += ' ' + this.cls;
31872         }
31873         
31874         var cfg = {
31875             tag: (this.href.length) ? 'a' : 'div',
31876             cls: cls,
31877             cn: [
31878                 {
31879                     tag: 'div',
31880                     cls: 'roo-brick-paragraph',
31881                     cn: []
31882                 }
31883             ]
31884         };
31885         
31886         if(this.href.length){
31887             cfg.href = this.href;
31888         }
31889         
31890         var cn = cfg.cn[0].cn;
31891         
31892         if(this.title.length){
31893             cn.push({
31894                 tag: 'h4',
31895                 cls: 'roo-brick-title',
31896                 html: this.title
31897             });
31898         }
31899         
31900         if(this.html.length){
31901             cn.push({
31902                 tag: 'p',
31903                 cls: 'roo-brick-text',
31904                 html: this.html
31905             });
31906         } else {
31907             cn.cls += ' hide';
31908         }
31909         
31910         if(this.bgimage.length){
31911             cfg.cn.push({
31912                 tag: 'img',
31913                 cls: 'roo-brick-image-view',
31914                 src: this.bgimage
31915             });
31916         }
31917         
31918         return cfg;
31919     },
31920     
31921     initEvents: function() 
31922     {
31923         if(this.title.length || this.html.length){
31924             this.el.on('mouseenter'  ,this.enter, this);
31925             this.el.on('mouseleave', this.leave, this);
31926         }
31927         
31928         
31929         Roo.EventManager.onWindowResize(this.resize, this); 
31930         
31931         this.resize();
31932     },
31933     
31934     resize : function()
31935     {
31936         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
31937         
31938         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
31939         
31940         if(this.bgimage.length){
31941             var image = this.el.select('.roo-brick-image-view', true).first();
31942             image.setWidth(paragraph.getWidth());
31943             image.setHeight(paragraph.getWidth());
31944             
31945             this.el.setHeight(paragraph.getWidth());
31946             
31947         }
31948         
31949     },
31950     
31951     enter: function(e, el)
31952     {
31953         e.preventDefault();
31954         
31955         if(this.bgimage.length){
31956             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
31957             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
31958         }
31959     },
31960     
31961     leave: function(e, el)
31962     {
31963         e.preventDefault();
31964         
31965         if(this.bgimage.length){
31966             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
31967             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
31968         }
31969     }
31970     
31971 });
31972
31973  
31974
31975  /*
31976  * - LGPL
31977  *
31978  * Input
31979  * 
31980  */
31981
31982 /**
31983  * @class Roo.bootstrap.NumberField
31984  * @extends Roo.bootstrap.Input
31985  * Bootstrap NumberField class
31986  * 
31987  * 
31988  * 
31989  * 
31990  * @constructor
31991  * Create a new NumberField
31992  * @param {Object} config The config object
31993  */
31994
31995 Roo.bootstrap.NumberField = function(config){
31996     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
31997 };
31998
31999 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32000     
32001     /**
32002      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32003      */
32004     allowDecimals : true,
32005     /**
32006      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32007      */
32008     decimalSeparator : ".",
32009     /**
32010      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32011      */
32012     decimalPrecision : 2,
32013     /**
32014      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32015      */
32016     allowNegative : true,
32017     /**
32018      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32019      */
32020     minValue : Number.NEGATIVE_INFINITY,
32021     /**
32022      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32023      */
32024     maxValue : Number.MAX_VALUE,
32025     /**
32026      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32027      */
32028     minText : "The minimum value for this field is {0}",
32029     /**
32030      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32031      */
32032     maxText : "The maximum value for this field is {0}",
32033     /**
32034      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
32035      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32036      */
32037     nanText : "{0} is not a valid number",
32038     /**
32039      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32040      */
32041     castInt : true,
32042
32043     // private
32044     initEvents : function()
32045     {   
32046         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32047         
32048         var allowed = "0123456789";
32049         
32050         if(this.allowDecimals){
32051             allowed += this.decimalSeparator;
32052         }
32053         
32054         if(this.allowNegative){
32055             allowed += "-";
32056         }
32057         
32058         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32059         
32060         var keyPress = function(e){
32061             
32062             var k = e.getKey();
32063             
32064             var c = e.getCharCode();
32065             
32066             if(
32067                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
32068                     allowed.indexOf(String.fromCharCode(c)) === -1
32069             ){
32070                 e.stopEvent();
32071                 return;
32072             }
32073             
32074             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
32075                 return;
32076             }
32077             
32078             if(allowed.indexOf(String.fromCharCode(c)) === -1){
32079                 e.stopEvent();
32080             }
32081         };
32082         
32083         this.el.on("keypress", keyPress, this);
32084     },
32085     
32086     validateValue : function(value)
32087     {
32088         
32089         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
32090             return false;
32091         }
32092         
32093         var num = this.parseValue(value);
32094         
32095         if(isNaN(num)){
32096             this.markInvalid(String.format(this.nanText, value));
32097             return false;
32098         }
32099         
32100         if(num < this.minValue){
32101             this.markInvalid(String.format(this.minText, this.minValue));
32102             return false;
32103         }
32104         
32105         if(num > this.maxValue){
32106             this.markInvalid(String.format(this.maxText, this.maxValue));
32107             return false;
32108         }
32109         
32110         return true;
32111     },
32112
32113     getValue : function()
32114     {
32115         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
32116     },
32117
32118     parseValue : function(value)
32119     {
32120         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
32121         return isNaN(value) ? '' : value;
32122     },
32123
32124     fixPrecision : function(value)
32125     {
32126         var nan = isNaN(value);
32127         
32128         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
32129             return nan ? '' : value;
32130         }
32131         return parseFloat(value).toFixed(this.decimalPrecision);
32132     },
32133
32134     setValue : function(v)
32135     {
32136         v = this.fixPrecision(v);
32137         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
32138     },
32139
32140     decimalPrecisionFcn : function(v)
32141     {
32142         return Math.floor(v);
32143     },
32144
32145     beforeBlur : function()
32146     {
32147         if(!this.castInt){
32148             return;
32149         }
32150         
32151         var v = this.parseValue(this.getRawValue());
32152         if(v){
32153             this.setValue(v);
32154         }
32155     }
32156     
32157 });
32158
32159  
32160
32161 /*
32162 * Licence: LGPL
32163 */
32164
32165 /**
32166  * @class Roo.bootstrap.DocumentSlider
32167  * @extends Roo.bootstrap.Component
32168  * Bootstrap DocumentSlider class
32169  * 
32170  * @constructor
32171  * Create a new DocumentViewer
32172  * @param {Object} config The config object
32173  */
32174
32175 Roo.bootstrap.DocumentSlider = function(config){
32176     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
32177     
32178     this.files = [];
32179     
32180     this.addEvents({
32181         /**
32182          * @event initial
32183          * Fire after initEvent
32184          * @param {Roo.bootstrap.DocumentSlider} this
32185          */
32186         "initial" : true,
32187         /**
32188          * @event update
32189          * Fire after update
32190          * @param {Roo.bootstrap.DocumentSlider} this
32191          */
32192         "update" : true,
32193         /**
32194          * @event click
32195          * Fire after click
32196          * @param {Roo.bootstrap.DocumentSlider} this
32197          */
32198         "click" : true
32199     });
32200 };
32201
32202 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
32203     
32204     files : false,
32205     
32206     indicator : 0,
32207     
32208     getAutoCreate : function()
32209     {
32210         var cfg = {
32211             tag : 'div',
32212             cls : 'roo-document-slider',
32213             cn : [
32214                 {
32215                     tag : 'div',
32216                     cls : 'roo-document-slider-header',
32217                     cn : [
32218                         {
32219                             tag : 'div',
32220                             cls : 'roo-document-slider-header-title'
32221                         }
32222                     ]
32223                 },
32224                 {
32225                     tag : 'div',
32226                     cls : 'roo-document-slider-body',
32227                     cn : [
32228                         {
32229                             tag : 'div',
32230                             cls : 'roo-document-slider-prev',
32231                             cn : [
32232                                 {
32233                                     tag : 'i',
32234                                     cls : 'fa fa-chevron-left'
32235                                 }
32236                             ]
32237                         },
32238                         {
32239                             tag : 'div',
32240                             cls : 'roo-document-slider-thumb',
32241                             cn : [
32242                                 {
32243                                     tag : 'img',
32244                                     cls : 'roo-document-slider-image'
32245                                 }
32246                             ]
32247                         },
32248                         {
32249                             tag : 'div',
32250                             cls : 'roo-document-slider-next',
32251                             cn : [
32252                                 {
32253                                     tag : 'i',
32254                                     cls : 'fa fa-chevron-right'
32255                                 }
32256                             ]
32257                         }
32258                     ]
32259                 }
32260             ]
32261         };
32262         
32263         return cfg;
32264     },
32265     
32266     initEvents : function()
32267     {
32268         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
32269         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
32270         
32271         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
32272         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
32273         
32274         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
32275         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32276         
32277         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
32278         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32279         
32280         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
32281         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32282         
32283         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
32284         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
32285         
32286         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
32287         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
32288         
32289         this.thumbEl.on('click', this.onClick, this);
32290         
32291         this.prevIndicator.on('click', this.prev, this);
32292         
32293         this.nextIndicator.on('click', this.next, this);
32294         
32295     },
32296     
32297     initial : function()
32298     {
32299         if(this.files.length){
32300             this.indicator = 1;
32301             this.update()
32302         }
32303         
32304         this.fireEvent('initial', this);
32305     },
32306     
32307     update : function()
32308     {
32309         this.imageEl.attr('src', this.files[this.indicator - 1]);
32310         
32311         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
32312         
32313         this.prevIndicator.show();
32314         
32315         if(this.indicator == 1){
32316             this.prevIndicator.hide();
32317         }
32318         
32319         this.nextIndicator.show();
32320         
32321         if(this.indicator == this.files.length){
32322             this.nextIndicator.hide();
32323         }
32324         
32325         this.thumbEl.scrollTo('top');
32326         
32327         this.fireEvent('update', this);
32328     },
32329     
32330     onClick : function(e)
32331     {
32332         e.preventDefault();
32333         
32334         this.fireEvent('click', this);
32335     },
32336     
32337     prev : function(e)
32338     {
32339         e.preventDefault();
32340         
32341         this.indicator = Math.max(1, this.indicator - 1);
32342         
32343         this.update();
32344     },
32345     
32346     next : function(e)
32347     {
32348         e.preventDefault();
32349         
32350         this.indicator = Math.min(this.files.length, this.indicator + 1);
32351         
32352         this.update();
32353     }
32354 });
32355 /*
32356  * - LGPL
32357  *
32358  * RadioSet
32359  *
32360  *
32361  */
32362
32363 /**
32364  * @class Roo.bootstrap.RadioSet
32365  * @extends Roo.bootstrap.Input
32366  * Bootstrap RadioSet class
32367  * @cfg {String} indicatorpos (left|right) default left
32368  * @cfg {Boolean} inline (true|false) inline the element (default true)
32369  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
32370  * @constructor
32371  * Create a new RadioSet
32372  * @param {Object} config The config object
32373  */
32374
32375 Roo.bootstrap.RadioSet = function(config){
32376     
32377     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
32378     
32379     this.radioes = [];
32380     
32381     Roo.bootstrap.RadioSet.register(this);
32382     
32383     this.addEvents({
32384         /**
32385         * @event check
32386         * Fires when the element is checked or unchecked.
32387         * @param {Roo.bootstrap.RadioSet} this This radio
32388         * @param {Roo.bootstrap.Radio} item The checked item
32389         */
32390        check : true
32391     });
32392     
32393 };
32394
32395 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
32396
32397     radioes : false,
32398     
32399     inline : true,
32400     
32401     weight : '',
32402     
32403     indicatorpos : 'left',
32404     
32405     getAutoCreate : function()
32406     {
32407         var label = {
32408             tag : 'label',
32409             cls : 'roo-radio-set-label',
32410             cn : [
32411                 {
32412                     tag : 'span',
32413                     html : this.fieldLabel
32414                 }
32415             ]
32416         };
32417         
32418         if(this.indicatorpos == 'left'){
32419             label.cn.unshift({
32420                 tag : 'i',
32421                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
32422                 tooltip : 'This field is required'
32423             });
32424         } else {
32425             label.cn.push({
32426                 tag : 'i',
32427                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
32428                 tooltip : 'This field is required'
32429             });
32430         }
32431         
32432         var items = {
32433             tag : 'div',
32434             cls : 'roo-radio-set-items'
32435         };
32436         
32437         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
32438         
32439         if (align === 'left' && this.fieldLabel.length) {
32440             
32441             items = {
32442                 cls : "roo-radio-set-right", 
32443                 cn: [
32444                     items
32445                 ]
32446             };
32447             
32448             if(this.labelWidth > 12){
32449                 label.style = "width: " + this.labelWidth + 'px';
32450             }
32451             
32452             if(this.labelWidth < 13 && this.labelmd == 0){
32453                 this.labelmd = this.labelWidth;
32454             }
32455             
32456             if(this.labellg > 0){
32457                 label.cls += ' col-lg-' + this.labellg;
32458                 items.cls += ' col-lg-' + (12 - this.labellg);
32459             }
32460             
32461             if(this.labelmd > 0){
32462                 label.cls += ' col-md-' + this.labelmd;
32463                 items.cls += ' col-md-' + (12 - this.labelmd);
32464             }
32465             
32466             if(this.labelsm > 0){
32467                 label.cls += ' col-sm-' + this.labelsm;
32468                 items.cls += ' col-sm-' + (12 - this.labelsm);
32469             }
32470             
32471             if(this.labelxs > 0){
32472                 label.cls += ' col-xs-' + this.labelxs;
32473                 items.cls += ' col-xs-' + (12 - this.labelxs);
32474             }
32475         }
32476         
32477         var cfg = {
32478             tag : 'div',
32479             cls : 'roo-radio-set',
32480             cn : [
32481                 {
32482                     tag : 'input',
32483                     cls : 'roo-radio-set-input',
32484                     type : 'text',
32485                     name : this.name,
32486                     value : this.value ? this.value :  ''
32487                 },
32488                 label,
32489                 items
32490             ]
32491         };
32492         
32493         if(this.weight.length){
32494             cfg.cls += ' roo-radio-' + this.weight;
32495         }
32496         
32497         if(this.inline) {
32498             cfg.cls += ' roo-radio-set-inline';
32499         }
32500         
32501         return cfg;
32502         
32503     },
32504
32505     initEvents : function()
32506     {
32507         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
32508         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
32509         
32510         if(!this.fieldLabel.length){
32511             this.labelEl.hide();
32512         }
32513         
32514         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
32515         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
32516         
32517         this.indicatorEl().setVisibilityMode(Roo.Element.DISPLAY);
32518         this.indicatorEl().hide();
32519         
32520         this.originalValue = this.getValue();
32521         
32522     },
32523     
32524     inputEl: function ()
32525     {
32526         return this.el.select('.roo-radio-set-input', true).first();
32527     },
32528     
32529     getChildContainer : function()
32530     {
32531         return this.itemsEl;
32532     },
32533     
32534     register : function(item)
32535     {
32536         this.radioes.push(item);
32537         
32538     },
32539     
32540     validate : function()
32541     {   
32542         var valid = false;
32543         
32544         Roo.each(this.radioes, function(i){
32545             if(!i.checked){
32546                 return;
32547             }
32548             
32549             valid = true;
32550             return false;
32551         });
32552         
32553         if(this.disabled || this.allowBlank || valid){
32554             this.markValid();
32555             return true;
32556         }
32557         
32558         this.markInvalid();
32559         return false;
32560         
32561     },
32562     
32563     markValid : function()
32564     {
32565         if(this.labelEl.isVisible(true)){
32566             this.indicatorEl().hide();
32567         }
32568         
32569         this.el.removeClass([this.invalidClass, this.validClass]);
32570         this.el.addClass(this.validClass);
32571         
32572         this.fireEvent('valid', this);
32573     },
32574     
32575     markInvalid : function(msg)
32576     {
32577         if(this.allowBlank || this.disabled){
32578             return;
32579         }
32580         
32581         if(this.labelEl.isVisible(true)){
32582             this.indicatorEl().show();
32583         }
32584         
32585         this.el.removeClass([this.invalidClass, this.validClass]);
32586         this.el.addClass(this.invalidClass);
32587         
32588         this.fireEvent('invalid', this, msg);
32589         
32590     },
32591     
32592     setValue : function(v, suppressEvent)
32593     {   
32594         Roo.each(this.radioes, function(i){
32595             
32596             i.checked = false;
32597             i.el.removeClass('checked');
32598             
32599             if(i.value === v || i.value.toString() === v.toString()){
32600                 i.checked = true;
32601                 i.el.addClass('checked');
32602                 
32603                 if(suppressEvent !== true){
32604                     this.fireEvent('check', this, i);
32605                 }
32606             }
32607             
32608         }, this);
32609         
32610         Roo.bootstrap.RadioSet.superclass.setValue.call(this, v);
32611         
32612     },
32613     
32614     clearInvalid : function(){
32615         
32616         if(!this.el || this.preventMark){
32617             return;
32618         }
32619         
32620         if(this.labelEl.isVisible(true)){
32621             this.indicatorEl().hide();
32622         }
32623         
32624         this.el.removeClass([this.invalidClass]);
32625         
32626         this.fireEvent('valid', this);
32627     }
32628     
32629 });
32630
32631 Roo.apply(Roo.bootstrap.RadioSet, {
32632     
32633     groups: {},
32634     
32635     register : function(set)
32636     {
32637         this.groups[set.name] = set;
32638     },
32639     
32640     get: function(name) 
32641     {
32642         if (typeof(this.groups[name]) == 'undefined') {
32643             return false;
32644         }
32645         
32646         return this.groups[name] ;
32647     }
32648     
32649 });
32650 /*
32651  * Based on:
32652  * Ext JS Library 1.1.1
32653  * Copyright(c) 2006-2007, Ext JS, LLC.
32654  *
32655  * Originally Released Under LGPL - original licence link has changed is not relivant.
32656  *
32657  * Fork - LGPL
32658  * <script type="text/javascript">
32659  */
32660
32661
32662 /**
32663  * @class Roo.bootstrap.SplitBar
32664  * @extends Roo.util.Observable
32665  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
32666  * <br><br>
32667  * Usage:
32668  * <pre><code>
32669 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
32670                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
32671 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
32672 split.minSize = 100;
32673 split.maxSize = 600;
32674 split.animate = true;
32675 split.on('moved', splitterMoved);
32676 </code></pre>
32677  * @constructor
32678  * Create a new SplitBar
32679  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
32680  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
32681  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32682  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
32683                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
32684                         position of the SplitBar).
32685  */
32686 Roo.bootstrap.SplitBar = function(cfg){
32687     
32688     /** @private */
32689     
32690     //{
32691     //  dragElement : elm
32692     //  resizingElement: el,
32693         // optional..
32694     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
32695     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
32696         // existingProxy ???
32697     //}
32698     
32699     this.el = Roo.get(cfg.dragElement, true);
32700     this.el.dom.unselectable = "on";
32701     /** @private */
32702     this.resizingEl = Roo.get(cfg.resizingElement, true);
32703
32704     /**
32705      * @private
32706      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32707      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
32708      * @type Number
32709      */
32710     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
32711     
32712     /**
32713      * The minimum size of the resizing element. (Defaults to 0)
32714      * @type Number
32715      */
32716     this.minSize = 0;
32717     
32718     /**
32719      * The maximum size of the resizing element. (Defaults to 2000)
32720      * @type Number
32721      */
32722     this.maxSize = 2000;
32723     
32724     /**
32725      * Whether to animate the transition to the new size
32726      * @type Boolean
32727      */
32728     this.animate = false;
32729     
32730     /**
32731      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
32732      * @type Boolean
32733      */
32734     this.useShim = false;
32735     
32736     /** @private */
32737     this.shim = null;
32738     
32739     if(!cfg.existingProxy){
32740         /** @private */
32741         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
32742     }else{
32743         this.proxy = Roo.get(cfg.existingProxy).dom;
32744     }
32745     /** @private */
32746     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
32747     
32748     /** @private */
32749     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
32750     
32751     /** @private */
32752     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
32753     
32754     /** @private */
32755     this.dragSpecs = {};
32756     
32757     /**
32758      * @private The adapter to use to positon and resize elements
32759      */
32760     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32761     this.adapter.init(this);
32762     
32763     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32764         /** @private */
32765         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
32766         this.el.addClass("roo-splitbar-h");
32767     }else{
32768         /** @private */
32769         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
32770         this.el.addClass("roo-splitbar-v");
32771     }
32772     
32773     this.addEvents({
32774         /**
32775          * @event resize
32776          * Fires when the splitter is moved (alias for {@link #event-moved})
32777          * @param {Roo.bootstrap.SplitBar} this
32778          * @param {Number} newSize the new width or height
32779          */
32780         "resize" : true,
32781         /**
32782          * @event moved
32783          * Fires when the splitter is moved
32784          * @param {Roo.bootstrap.SplitBar} this
32785          * @param {Number} newSize the new width or height
32786          */
32787         "moved" : true,
32788         /**
32789          * @event beforeresize
32790          * Fires before the splitter is dragged
32791          * @param {Roo.bootstrap.SplitBar} this
32792          */
32793         "beforeresize" : true,
32794
32795         "beforeapply" : true
32796     });
32797
32798     Roo.util.Observable.call(this);
32799 };
32800
32801 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
32802     onStartProxyDrag : function(x, y){
32803         this.fireEvent("beforeresize", this);
32804         if(!this.overlay){
32805             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
32806             o.unselectable();
32807             o.enableDisplayMode("block");
32808             // all splitbars share the same overlay
32809             Roo.bootstrap.SplitBar.prototype.overlay = o;
32810         }
32811         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32812         this.overlay.show();
32813         Roo.get(this.proxy).setDisplayed("block");
32814         var size = this.adapter.getElementSize(this);
32815         this.activeMinSize = this.getMinimumSize();;
32816         this.activeMaxSize = this.getMaximumSize();;
32817         var c1 = size - this.activeMinSize;
32818         var c2 = Math.max(this.activeMaxSize - size, 0);
32819         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32820             this.dd.resetConstraints();
32821             this.dd.setXConstraint(
32822                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
32823                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
32824             );
32825             this.dd.setYConstraint(0, 0);
32826         }else{
32827             this.dd.resetConstraints();
32828             this.dd.setXConstraint(0, 0);
32829             this.dd.setYConstraint(
32830                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
32831                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
32832             );
32833          }
32834         this.dragSpecs.startSize = size;
32835         this.dragSpecs.startPoint = [x, y];
32836         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
32837     },
32838     
32839     /** 
32840      * @private Called after the drag operation by the DDProxy
32841      */
32842     onEndProxyDrag : function(e){
32843         Roo.get(this.proxy).setDisplayed(false);
32844         var endPoint = Roo.lib.Event.getXY(e);
32845         if(this.overlay){
32846             this.overlay.hide();
32847         }
32848         var newSize;
32849         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32850             newSize = this.dragSpecs.startSize + 
32851                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
32852                     endPoint[0] - this.dragSpecs.startPoint[0] :
32853                     this.dragSpecs.startPoint[0] - endPoint[0]
32854                 );
32855         }else{
32856             newSize = this.dragSpecs.startSize + 
32857                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
32858                     endPoint[1] - this.dragSpecs.startPoint[1] :
32859                     this.dragSpecs.startPoint[1] - endPoint[1]
32860                 );
32861         }
32862         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
32863         if(newSize != this.dragSpecs.startSize){
32864             if(this.fireEvent('beforeapply', this, newSize) !== false){
32865                 this.adapter.setElementSize(this, newSize);
32866                 this.fireEvent("moved", this, newSize);
32867                 this.fireEvent("resize", this, newSize);
32868             }
32869         }
32870     },
32871     
32872     /**
32873      * Get the adapter this SplitBar uses
32874      * @return The adapter object
32875      */
32876     getAdapter : function(){
32877         return this.adapter;
32878     },
32879     
32880     /**
32881      * Set the adapter this SplitBar uses
32882      * @param {Object} adapter A SplitBar adapter object
32883      */
32884     setAdapter : function(adapter){
32885         this.adapter = adapter;
32886         this.adapter.init(this);
32887     },
32888     
32889     /**
32890      * Gets the minimum size for the resizing element
32891      * @return {Number} The minimum size
32892      */
32893     getMinimumSize : function(){
32894         return this.minSize;
32895     },
32896     
32897     /**
32898      * Sets the minimum size for the resizing element
32899      * @param {Number} minSize The minimum size
32900      */
32901     setMinimumSize : function(minSize){
32902         this.minSize = minSize;
32903     },
32904     
32905     /**
32906      * Gets the maximum size for the resizing element
32907      * @return {Number} The maximum size
32908      */
32909     getMaximumSize : function(){
32910         return this.maxSize;
32911     },
32912     
32913     /**
32914      * Sets the maximum size for the resizing element
32915      * @param {Number} maxSize The maximum size
32916      */
32917     setMaximumSize : function(maxSize){
32918         this.maxSize = maxSize;
32919     },
32920     
32921     /**
32922      * Sets the initialize size for the resizing element
32923      * @param {Number} size The initial size
32924      */
32925     setCurrentSize : function(size){
32926         var oldAnimate = this.animate;
32927         this.animate = false;
32928         this.adapter.setElementSize(this, size);
32929         this.animate = oldAnimate;
32930     },
32931     
32932     /**
32933      * Destroy this splitbar. 
32934      * @param {Boolean} removeEl True to remove the element
32935      */
32936     destroy : function(removeEl){
32937         if(this.shim){
32938             this.shim.remove();
32939         }
32940         this.dd.unreg();
32941         this.proxy.parentNode.removeChild(this.proxy);
32942         if(removeEl){
32943             this.el.remove();
32944         }
32945     }
32946 });
32947
32948 /**
32949  * @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.
32950  */
32951 Roo.bootstrap.SplitBar.createProxy = function(dir){
32952     var proxy = new Roo.Element(document.createElement("div"));
32953     proxy.unselectable();
32954     var cls = 'roo-splitbar-proxy';
32955     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
32956     document.body.appendChild(proxy.dom);
32957     return proxy.dom;
32958 };
32959
32960 /** 
32961  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
32962  * Default Adapter. It assumes the splitter and resizing element are not positioned
32963  * elements and only gets/sets the width of the element. Generally used for table based layouts.
32964  */
32965 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
32966 };
32967
32968 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
32969     // do nothing for now
32970     init : function(s){
32971     
32972     },
32973     /**
32974      * Called before drag operations to get the current size of the resizing element. 
32975      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32976      */
32977      getElementSize : function(s){
32978         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32979             return s.resizingEl.getWidth();
32980         }else{
32981             return s.resizingEl.getHeight();
32982         }
32983     },
32984     
32985     /**
32986      * Called after drag operations to set the size of the resizing element.
32987      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32988      * @param {Number} newSize The new size to set
32989      * @param {Function} onComplete A function to be invoked when resizing is complete
32990      */
32991     setElementSize : function(s, newSize, onComplete){
32992         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32993             if(!s.animate){
32994                 s.resizingEl.setWidth(newSize);
32995                 if(onComplete){
32996                     onComplete(s, newSize);
32997                 }
32998             }else{
32999                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33000             }
33001         }else{
33002             
33003             if(!s.animate){
33004                 s.resizingEl.setHeight(newSize);
33005                 if(onComplete){
33006                     onComplete(s, newSize);
33007                 }
33008             }else{
33009                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33010             }
33011         }
33012     }
33013 };
33014
33015 /** 
33016  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33017  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33018  * Adapter that  moves the splitter element to align with the resized sizing element. 
33019  * Used with an absolute positioned SplitBar.
33020  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33021  * document.body, make sure you assign an id to the body element.
33022  */
33023 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33024     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33025     this.container = Roo.get(container);
33026 };
33027
33028 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33029     init : function(s){
33030         this.basic.init(s);
33031     },
33032     
33033     getElementSize : function(s){
33034         return this.basic.getElementSize(s);
33035     },
33036     
33037     setElementSize : function(s, newSize, onComplete){
33038         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33039     },
33040     
33041     moveSplitter : function(s){
33042         var yes = Roo.bootstrap.SplitBar;
33043         switch(s.placement){
33044             case yes.LEFT:
33045                 s.el.setX(s.resizingEl.getRight());
33046                 break;
33047             case yes.RIGHT:
33048                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
33049                 break;
33050             case yes.TOP:
33051                 s.el.setY(s.resizingEl.getBottom());
33052                 break;
33053             case yes.BOTTOM:
33054                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
33055                 break;
33056         }
33057     }
33058 };
33059
33060 /**
33061  * Orientation constant - Create a vertical SplitBar
33062  * @static
33063  * @type Number
33064  */
33065 Roo.bootstrap.SplitBar.VERTICAL = 1;
33066
33067 /**
33068  * Orientation constant - Create a horizontal SplitBar
33069  * @static
33070  * @type Number
33071  */
33072 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
33073
33074 /**
33075  * Placement constant - The resizing element is to the left of the splitter element
33076  * @static
33077  * @type Number
33078  */
33079 Roo.bootstrap.SplitBar.LEFT = 1;
33080
33081 /**
33082  * Placement constant - The resizing element is to the right of the splitter element
33083  * @static
33084  * @type Number
33085  */
33086 Roo.bootstrap.SplitBar.RIGHT = 2;
33087
33088 /**
33089  * Placement constant - The resizing element is positioned above the splitter element
33090  * @static
33091  * @type Number
33092  */
33093 Roo.bootstrap.SplitBar.TOP = 3;
33094
33095 /**
33096  * Placement constant - The resizing element is positioned under splitter element
33097  * @static
33098  * @type Number
33099  */
33100 Roo.bootstrap.SplitBar.BOTTOM = 4;
33101 Roo.namespace("Roo.bootstrap.layout");/*
33102  * Based on:
33103  * Ext JS Library 1.1.1
33104  * Copyright(c) 2006-2007, Ext JS, LLC.
33105  *
33106  * Originally Released Under LGPL - original licence link has changed is not relivant.
33107  *
33108  * Fork - LGPL
33109  * <script type="text/javascript">
33110  */
33111
33112 /**
33113  * @class Roo.bootstrap.layout.Manager
33114  * @extends Roo.bootstrap.Component
33115  * Base class for layout managers.
33116  */
33117 Roo.bootstrap.layout.Manager = function(config)
33118 {
33119     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
33120
33121
33122
33123
33124
33125     /** false to disable window resize monitoring @type Boolean */
33126     this.monitorWindowResize = true;
33127     this.regions = {};
33128     this.addEvents({
33129         /**
33130          * @event layout
33131          * Fires when a layout is performed.
33132          * @param {Roo.LayoutManager} this
33133          */
33134         "layout" : true,
33135         /**
33136          * @event regionresized
33137          * Fires when the user resizes a region.
33138          * @param {Roo.LayoutRegion} region The resized region
33139          * @param {Number} newSize The new size (width for east/west, height for north/south)
33140          */
33141         "regionresized" : true,
33142         /**
33143          * @event regioncollapsed
33144          * Fires when a region is collapsed.
33145          * @param {Roo.LayoutRegion} region The collapsed region
33146          */
33147         "regioncollapsed" : true,
33148         /**
33149          * @event regionexpanded
33150          * Fires when a region is expanded.
33151          * @param {Roo.LayoutRegion} region The expanded region
33152          */
33153         "regionexpanded" : true
33154     });
33155     this.updating = false;
33156
33157     if (config.el) {
33158         this.el = Roo.get(config.el);
33159         this.initEvents();
33160     }
33161
33162 };
33163
33164 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
33165
33166
33167     regions : null,
33168
33169     monitorWindowResize : true,
33170
33171
33172     updating : false,
33173
33174
33175     onRender : function(ct, position)
33176     {
33177         if(!this.el){
33178             this.el = Roo.get(ct);
33179             this.initEvents();
33180         }
33181         //this.fireEvent('render',this);
33182     },
33183
33184
33185     initEvents: function()
33186     {
33187
33188
33189         // ie scrollbar fix
33190         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
33191             document.body.scroll = "no";
33192         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
33193             this.el.position('relative');
33194         }
33195         this.id = this.el.id;
33196         this.el.addClass("roo-layout-container");
33197         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33198         if(this.el.dom != document.body ) {
33199             this.el.on('resize', this.layout,this);
33200             this.el.on('show', this.layout,this);
33201         }
33202
33203     },
33204
33205     /**
33206      * Returns true if this layout is currently being updated
33207      * @return {Boolean}
33208      */
33209     isUpdating : function(){
33210         return this.updating;
33211     },
33212
33213     /**
33214      * Suspend the LayoutManager from doing auto-layouts while
33215      * making multiple add or remove calls
33216      */
33217     beginUpdate : function(){
33218         this.updating = true;
33219     },
33220
33221     /**
33222      * Restore auto-layouts and optionally disable the manager from performing a layout
33223      * @param {Boolean} noLayout true to disable a layout update
33224      */
33225     endUpdate : function(noLayout){
33226         this.updating = false;
33227         if(!noLayout){
33228             this.layout();
33229         }
33230     },
33231
33232     layout: function(){
33233         // abstract...
33234     },
33235
33236     onRegionResized : function(region, newSize){
33237         this.fireEvent("regionresized", region, newSize);
33238         this.layout();
33239     },
33240
33241     onRegionCollapsed : function(region){
33242         this.fireEvent("regioncollapsed", region);
33243     },
33244
33245     onRegionExpanded : function(region){
33246         this.fireEvent("regionexpanded", region);
33247     },
33248
33249     /**
33250      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
33251      * performs box-model adjustments.
33252      * @return {Object} The size as an object {width: (the width), height: (the height)}
33253      */
33254     getViewSize : function()
33255     {
33256         var size;
33257         if(this.el.dom != document.body){
33258             size = this.el.getSize();
33259         }else{
33260             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
33261         }
33262         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
33263         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
33264         return size;
33265     },
33266
33267     /**
33268      * Returns the Element this layout is bound to.
33269      * @return {Roo.Element}
33270      */
33271     getEl : function(){
33272         return this.el;
33273     },
33274
33275     /**
33276      * Returns the specified region.
33277      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
33278      * @return {Roo.LayoutRegion}
33279      */
33280     getRegion : function(target){
33281         return this.regions[target.toLowerCase()];
33282     },
33283
33284     onWindowResize : function(){
33285         if(this.monitorWindowResize){
33286             this.layout();
33287         }
33288     }
33289 });
33290 /*
33291  * Based on:
33292  * Ext JS Library 1.1.1
33293  * Copyright(c) 2006-2007, Ext JS, LLC.
33294  *
33295  * Originally Released Under LGPL - original licence link has changed is not relivant.
33296  *
33297  * Fork - LGPL
33298  * <script type="text/javascript">
33299  */
33300 /**
33301  * @class Roo.bootstrap.layout.Border
33302  * @extends Roo.bootstrap.layout.Manager
33303  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
33304  * please see: examples/bootstrap/nested.html<br><br>
33305  
33306 <b>The container the layout is rendered into can be either the body element or any other element.
33307 If it is not the body element, the container needs to either be an absolute positioned element,
33308 or you will need to add "position:relative" to the css of the container.  You will also need to specify
33309 the container size if it is not the body element.</b>
33310
33311 * @constructor
33312 * Create a new Border
33313 * @param {Object} config Configuration options
33314  */
33315 Roo.bootstrap.layout.Border = function(config){
33316     config = config || {};
33317     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
33318     
33319     
33320     
33321     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
33322         if(config[region]){
33323             config[region].region = region;
33324             this.addRegion(config[region]);
33325         }
33326     },this);
33327     
33328 };
33329
33330 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
33331
33332 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
33333     /**
33334      * Creates and adds a new region if it doesn't already exist.
33335      * @param {String} target The target region key (north, south, east, west or center).
33336      * @param {Object} config The regions config object
33337      * @return {BorderLayoutRegion} The new region
33338      */
33339     addRegion : function(config)
33340     {
33341         if(!this.regions[config.region]){
33342             var r = this.factory(config);
33343             this.bindRegion(r);
33344         }
33345         return this.regions[config.region];
33346     },
33347
33348     // private (kinda)
33349     bindRegion : function(r){
33350         this.regions[r.config.region] = r;
33351         
33352         r.on("visibilitychange",    this.layout, this);
33353         r.on("paneladded",          this.layout, this);
33354         r.on("panelremoved",        this.layout, this);
33355         r.on("invalidated",         this.layout, this);
33356         r.on("resized",             this.onRegionResized, this);
33357         r.on("collapsed",           this.onRegionCollapsed, this);
33358         r.on("expanded",            this.onRegionExpanded, this);
33359     },
33360
33361     /**
33362      * Performs a layout update.
33363      */
33364     layout : function()
33365     {
33366         if(this.updating) {
33367             return;
33368         }
33369         
33370         // render all the rebions if they have not been done alreayd?
33371         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
33372             if(this.regions[region] && !this.regions[region].bodyEl){
33373                 this.regions[region].onRender(this.el)
33374             }
33375         },this);
33376         
33377         var size = this.getViewSize();
33378         var w = size.width;
33379         var h = size.height;
33380         var centerW = w;
33381         var centerH = h;
33382         var centerY = 0;
33383         var centerX = 0;
33384         //var x = 0, y = 0;
33385
33386         var rs = this.regions;
33387         var north = rs["north"];
33388         var south = rs["south"]; 
33389         var west = rs["west"];
33390         var east = rs["east"];
33391         var center = rs["center"];
33392         //if(this.hideOnLayout){ // not supported anymore
33393             //c.el.setStyle("display", "none");
33394         //}
33395         if(north && north.isVisible()){
33396             var b = north.getBox();
33397             var m = north.getMargins();
33398             b.width = w - (m.left+m.right);
33399             b.x = m.left;
33400             b.y = m.top;
33401             centerY = b.height + b.y + m.bottom;
33402             centerH -= centerY;
33403             north.updateBox(this.safeBox(b));
33404         }
33405         if(south && south.isVisible()){
33406             var b = south.getBox();
33407             var m = south.getMargins();
33408             b.width = w - (m.left+m.right);
33409             b.x = m.left;
33410             var totalHeight = (b.height + m.top + m.bottom);
33411             b.y = h - totalHeight + m.top;
33412             centerH -= totalHeight;
33413             south.updateBox(this.safeBox(b));
33414         }
33415         if(west && west.isVisible()){
33416             var b = west.getBox();
33417             var m = west.getMargins();
33418             b.height = centerH - (m.top+m.bottom);
33419             b.x = m.left;
33420             b.y = centerY + m.top;
33421             var totalWidth = (b.width + m.left + m.right);
33422             centerX += totalWidth;
33423             centerW -= totalWidth;
33424             west.updateBox(this.safeBox(b));
33425         }
33426         if(east && east.isVisible()){
33427             var b = east.getBox();
33428             var m = east.getMargins();
33429             b.height = centerH - (m.top+m.bottom);
33430             var totalWidth = (b.width + m.left + m.right);
33431             b.x = w - totalWidth + m.left;
33432             b.y = centerY + m.top;
33433             centerW -= totalWidth;
33434             east.updateBox(this.safeBox(b));
33435         }
33436         if(center){
33437             var m = center.getMargins();
33438             var centerBox = {
33439                 x: centerX + m.left,
33440                 y: centerY + m.top,
33441                 width: centerW - (m.left+m.right),
33442                 height: centerH - (m.top+m.bottom)
33443             };
33444             //if(this.hideOnLayout){
33445                 //center.el.setStyle("display", "block");
33446             //}
33447             center.updateBox(this.safeBox(centerBox));
33448         }
33449         this.el.repaint();
33450         this.fireEvent("layout", this);
33451     },
33452
33453     // private
33454     safeBox : function(box){
33455         box.width = Math.max(0, box.width);
33456         box.height = Math.max(0, box.height);
33457         return box;
33458     },
33459
33460     /**
33461      * Adds a ContentPanel (or subclass) to this layout.
33462      * @param {String} target The target region key (north, south, east, west or center).
33463      * @param {Roo.ContentPanel} panel The panel to add
33464      * @return {Roo.ContentPanel} The added panel
33465      */
33466     add : function(target, panel){
33467          
33468         target = target.toLowerCase();
33469         return this.regions[target].add(panel);
33470     },
33471
33472     /**
33473      * Remove a ContentPanel (or subclass) to this layout.
33474      * @param {String} target The target region key (north, south, east, west or center).
33475      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
33476      * @return {Roo.ContentPanel} The removed panel
33477      */
33478     remove : function(target, panel){
33479         target = target.toLowerCase();
33480         return this.regions[target].remove(panel);
33481     },
33482
33483     /**
33484      * Searches all regions for a panel with the specified id
33485      * @param {String} panelId
33486      * @return {Roo.ContentPanel} The panel or null if it wasn't found
33487      */
33488     findPanel : function(panelId){
33489         var rs = this.regions;
33490         for(var target in rs){
33491             if(typeof rs[target] != "function"){
33492                 var p = rs[target].getPanel(panelId);
33493                 if(p){
33494                     return p;
33495                 }
33496             }
33497         }
33498         return null;
33499     },
33500
33501     /**
33502      * Searches all regions for a panel with the specified id and activates (shows) it.
33503      * @param {String/ContentPanel} panelId The panels id or the panel itself
33504      * @return {Roo.ContentPanel} The shown panel or null
33505      */
33506     showPanel : function(panelId) {
33507       var rs = this.regions;
33508       for(var target in rs){
33509          var r = rs[target];
33510          if(typeof r != "function"){
33511             if(r.hasPanel(panelId)){
33512                return r.showPanel(panelId);
33513             }
33514          }
33515       }
33516       return null;
33517    },
33518
33519    /**
33520      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
33521      * @param {Roo.state.Provider} provider (optional) An alternate state provider
33522      */
33523    /*
33524     restoreState : function(provider){
33525         if(!provider){
33526             provider = Roo.state.Manager;
33527         }
33528         var sm = new Roo.LayoutStateManager();
33529         sm.init(this, provider);
33530     },
33531 */
33532  
33533  
33534     /**
33535      * Adds a xtype elements to the layout.
33536      * <pre><code>
33537
33538 layout.addxtype({
33539        xtype : 'ContentPanel',
33540        region: 'west',
33541        items: [ .... ]
33542    }
33543 );
33544
33545 layout.addxtype({
33546         xtype : 'NestedLayoutPanel',
33547         region: 'west',
33548         layout: {
33549            center: { },
33550            west: { }   
33551         },
33552         items : [ ... list of content panels or nested layout panels.. ]
33553    }
33554 );
33555 </code></pre>
33556      * @param {Object} cfg Xtype definition of item to add.
33557      */
33558     addxtype : function(cfg)
33559     {
33560         // basically accepts a pannel...
33561         // can accept a layout region..!?!?
33562         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
33563         
33564         
33565         // theory?  children can only be panels??
33566         
33567         //if (!cfg.xtype.match(/Panel$/)) {
33568         //    return false;
33569         //}
33570         var ret = false;
33571         
33572         if (typeof(cfg.region) == 'undefined') {
33573             Roo.log("Failed to add Panel, region was not set");
33574             Roo.log(cfg);
33575             return false;
33576         }
33577         var region = cfg.region;
33578         delete cfg.region;
33579         
33580           
33581         var xitems = [];
33582         if (cfg.items) {
33583             xitems = cfg.items;
33584             delete cfg.items;
33585         }
33586         var nb = false;
33587         
33588         switch(cfg.xtype) 
33589         {
33590             case 'Content':  // ContentPanel (el, cfg)
33591             case 'Scroll':  // ContentPanel (el, cfg)
33592             case 'View': 
33593                 cfg.autoCreate = true;
33594                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33595                 //} else {
33596                 //    var el = this.el.createChild();
33597                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
33598                 //}
33599                 
33600                 this.add(region, ret);
33601                 break;
33602             
33603             /*
33604             case 'TreePanel': // our new panel!
33605                 cfg.el = this.el.createChild();
33606                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33607                 this.add(region, ret);
33608                 break;
33609             */
33610             
33611             case 'Nest': 
33612                 // create a new Layout (which is  a Border Layout...
33613                 
33614                 var clayout = cfg.layout;
33615                 clayout.el  = this.el.createChild();
33616                 clayout.items   = clayout.items  || [];
33617                 
33618                 delete cfg.layout;
33619                 
33620                 // replace this exitems with the clayout ones..
33621                 xitems = clayout.items;
33622                  
33623                 // force background off if it's in center...
33624                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33625                     cfg.background = false;
33626                 }
33627                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
33628                 
33629                 
33630                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33631                 //console.log('adding nested layout panel '  + cfg.toSource());
33632                 this.add(region, ret);
33633                 nb = {}; /// find first...
33634                 break;
33635             
33636             case 'Grid':
33637                 
33638                 // needs grid and region
33639                 
33640                 //var el = this.getRegion(region).el.createChild();
33641                 /*
33642                  *var el = this.el.createChild();
33643                 // create the grid first...
33644                 cfg.grid.container = el;
33645                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
33646                 */
33647                 
33648                 if (region == 'center' && this.active ) {
33649                     cfg.background = false;
33650                 }
33651                 
33652                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33653                 
33654                 this.add(region, ret);
33655                 /*
33656                 if (cfg.background) {
33657                     // render grid on panel activation (if panel background)
33658                     ret.on('activate', function(gp) {
33659                         if (!gp.grid.rendered) {
33660                     //        gp.grid.render(el);
33661                         }
33662                     });
33663                 } else {
33664                   //  cfg.grid.render(el);
33665                 }
33666                 */
33667                 break;
33668            
33669            
33670             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
33671                 // it was the old xcomponent building that caused this before.
33672                 // espeically if border is the top element in the tree.
33673                 ret = this;
33674                 break; 
33675                 
33676                     
33677                 
33678                 
33679                 
33680             default:
33681                 /*
33682                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33683                     
33684                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33685                     this.add(region, ret);
33686                 } else {
33687                 */
33688                     Roo.log(cfg);
33689                     throw "Can not add '" + cfg.xtype + "' to Border";
33690                     return null;
33691              
33692                                 
33693              
33694         }
33695         this.beginUpdate();
33696         // add children..
33697         var region = '';
33698         var abn = {};
33699         Roo.each(xitems, function(i)  {
33700             region = nb && i.region ? i.region : false;
33701             
33702             var add = ret.addxtype(i);
33703            
33704             if (region) {
33705                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33706                 if (!i.background) {
33707                     abn[region] = nb[region] ;
33708                 }
33709             }
33710             
33711         });
33712         this.endUpdate();
33713
33714         // make the last non-background panel active..
33715         //if (nb) { Roo.log(abn); }
33716         if (nb) {
33717             
33718             for(var r in abn) {
33719                 region = this.getRegion(r);
33720                 if (region) {
33721                     // tried using nb[r], but it does not work..
33722                      
33723                     region.showPanel(abn[r]);
33724                    
33725                 }
33726             }
33727         }
33728         return ret;
33729         
33730     },
33731     
33732     
33733 // private
33734     factory : function(cfg)
33735     {
33736         
33737         var validRegions = Roo.bootstrap.layout.Border.regions;
33738
33739         var target = cfg.region;
33740         cfg.mgr = this;
33741         
33742         var r = Roo.bootstrap.layout;
33743         Roo.log(target);
33744         switch(target){
33745             case "north":
33746                 return new r.North(cfg);
33747             case "south":
33748                 return new r.South(cfg);
33749             case "east":
33750                 return new r.East(cfg);
33751             case "west":
33752                 return new r.West(cfg);
33753             case "center":
33754                 return new r.Center(cfg);
33755         }
33756         throw 'Layout region "'+target+'" not supported.';
33757     }
33758     
33759     
33760 });
33761  /*
33762  * Based on:
33763  * Ext JS Library 1.1.1
33764  * Copyright(c) 2006-2007, Ext JS, LLC.
33765  *
33766  * Originally Released Under LGPL - original licence link has changed is not relivant.
33767  *
33768  * Fork - LGPL
33769  * <script type="text/javascript">
33770  */
33771  
33772 /**
33773  * @class Roo.bootstrap.layout.Basic
33774  * @extends Roo.util.Observable
33775  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33776  * and does not have a titlebar, tabs or any other features. All it does is size and position 
33777  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33778  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
33779  * @cfg {string}   region  the region that it inhabits..
33780  * @cfg {bool}   skipConfig skip config?
33781  * 
33782
33783  */
33784 Roo.bootstrap.layout.Basic = function(config){
33785     
33786     this.mgr = config.mgr;
33787     
33788     this.position = config.region;
33789     
33790     var skipConfig = config.skipConfig;
33791     
33792     this.events = {
33793         /**
33794          * @scope Roo.BasicLayoutRegion
33795          */
33796         
33797         /**
33798          * @event beforeremove
33799          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33800          * @param {Roo.LayoutRegion} this
33801          * @param {Roo.ContentPanel} panel The panel
33802          * @param {Object} e The cancel event object
33803          */
33804         "beforeremove" : true,
33805         /**
33806          * @event invalidated
33807          * Fires when the layout for this region is changed.
33808          * @param {Roo.LayoutRegion} this
33809          */
33810         "invalidated" : true,
33811         /**
33812          * @event visibilitychange
33813          * Fires when this region is shown or hidden 
33814          * @param {Roo.LayoutRegion} this
33815          * @param {Boolean} visibility true or false
33816          */
33817         "visibilitychange" : true,
33818         /**
33819          * @event paneladded
33820          * Fires when a panel is added. 
33821          * @param {Roo.LayoutRegion} this
33822          * @param {Roo.ContentPanel} panel The panel
33823          */
33824         "paneladded" : true,
33825         /**
33826          * @event panelremoved
33827          * Fires when a panel is removed. 
33828          * @param {Roo.LayoutRegion} this
33829          * @param {Roo.ContentPanel} panel The panel
33830          */
33831         "panelremoved" : true,
33832         /**
33833          * @event beforecollapse
33834          * Fires when this region before collapse.
33835          * @param {Roo.LayoutRegion} this
33836          */
33837         "beforecollapse" : true,
33838         /**
33839          * @event collapsed
33840          * Fires when this region is collapsed.
33841          * @param {Roo.LayoutRegion} this
33842          */
33843         "collapsed" : true,
33844         /**
33845          * @event expanded
33846          * Fires when this region is expanded.
33847          * @param {Roo.LayoutRegion} this
33848          */
33849         "expanded" : true,
33850         /**
33851          * @event slideshow
33852          * Fires when this region is slid into view.
33853          * @param {Roo.LayoutRegion} this
33854          */
33855         "slideshow" : true,
33856         /**
33857          * @event slidehide
33858          * Fires when this region slides out of view. 
33859          * @param {Roo.LayoutRegion} this
33860          */
33861         "slidehide" : true,
33862         /**
33863          * @event panelactivated
33864          * Fires when a panel is activated. 
33865          * @param {Roo.LayoutRegion} this
33866          * @param {Roo.ContentPanel} panel The activated panel
33867          */
33868         "panelactivated" : true,
33869         /**
33870          * @event resized
33871          * Fires when the user resizes this region. 
33872          * @param {Roo.LayoutRegion} this
33873          * @param {Number} newSize The new size (width for east/west, height for north/south)
33874          */
33875         "resized" : true
33876     };
33877     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33878     this.panels = new Roo.util.MixedCollection();
33879     this.panels.getKey = this.getPanelId.createDelegate(this);
33880     this.box = null;
33881     this.activePanel = null;
33882     // ensure listeners are added...
33883     
33884     if (config.listeners || config.events) {
33885         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
33886             listeners : config.listeners || {},
33887             events : config.events || {}
33888         });
33889     }
33890     
33891     if(skipConfig !== true){
33892         this.applyConfig(config);
33893     }
33894 };
33895
33896 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
33897 {
33898     getPanelId : function(p){
33899         return p.getId();
33900     },
33901     
33902     applyConfig : function(config){
33903         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33904         this.config = config;
33905         
33906     },
33907     
33908     /**
33909      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33910      * the width, for horizontal (north, south) the height.
33911      * @param {Number} newSize The new width or height
33912      */
33913     resizeTo : function(newSize){
33914         var el = this.el ? this.el :
33915                  (this.activePanel ? this.activePanel.getEl() : null);
33916         if(el){
33917             switch(this.position){
33918                 case "east":
33919                 case "west":
33920                     el.setWidth(newSize);
33921                     this.fireEvent("resized", this, newSize);
33922                 break;
33923                 case "north":
33924                 case "south":
33925                     el.setHeight(newSize);
33926                     this.fireEvent("resized", this, newSize);
33927                 break;                
33928             }
33929         }
33930     },
33931     
33932     getBox : function(){
33933         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33934     },
33935     
33936     getMargins : function(){
33937         return this.margins;
33938     },
33939     
33940     updateBox : function(box){
33941         this.box = box;
33942         var el = this.activePanel.getEl();
33943         el.dom.style.left = box.x + "px";
33944         el.dom.style.top = box.y + "px";
33945         this.activePanel.setSize(box.width, box.height);
33946     },
33947     
33948     /**
33949      * Returns the container element for this region.
33950      * @return {Roo.Element}
33951      */
33952     getEl : function(){
33953         return this.activePanel;
33954     },
33955     
33956     /**
33957      * Returns true if this region is currently visible.
33958      * @return {Boolean}
33959      */
33960     isVisible : function(){
33961         return this.activePanel ? true : false;
33962     },
33963     
33964     setActivePanel : function(panel){
33965         panel = this.getPanel(panel);
33966         if(this.activePanel && this.activePanel != panel){
33967             this.activePanel.setActiveState(false);
33968             this.activePanel.getEl().setLeftTop(-10000,-10000);
33969         }
33970         this.activePanel = panel;
33971         panel.setActiveState(true);
33972         if(this.box){
33973             panel.setSize(this.box.width, this.box.height);
33974         }
33975         this.fireEvent("panelactivated", this, panel);
33976         this.fireEvent("invalidated");
33977     },
33978     
33979     /**
33980      * Show the specified panel.
33981      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33982      * @return {Roo.ContentPanel} The shown panel or null
33983      */
33984     showPanel : function(panel){
33985         panel = this.getPanel(panel);
33986         if(panel){
33987             this.setActivePanel(panel);
33988         }
33989         return panel;
33990     },
33991     
33992     /**
33993      * Get the active panel for this region.
33994      * @return {Roo.ContentPanel} The active panel or null
33995      */
33996     getActivePanel : function(){
33997         return this.activePanel;
33998     },
33999     
34000     /**
34001      * Add the passed ContentPanel(s)
34002      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34003      * @return {Roo.ContentPanel} The panel added (if only one was added)
34004      */
34005     add : function(panel){
34006         if(arguments.length > 1){
34007             for(var i = 0, len = arguments.length; i < len; i++) {
34008                 this.add(arguments[i]);
34009             }
34010             return null;
34011         }
34012         if(this.hasPanel(panel)){
34013             this.showPanel(panel);
34014             return panel;
34015         }
34016         var el = panel.getEl();
34017         if(el.dom.parentNode != this.mgr.el.dom){
34018             this.mgr.el.dom.appendChild(el.dom);
34019         }
34020         if(panel.setRegion){
34021             panel.setRegion(this);
34022         }
34023         this.panels.add(panel);
34024         el.setStyle("position", "absolute");
34025         if(!panel.background){
34026             this.setActivePanel(panel);
34027             if(this.config.initialSize && this.panels.getCount()==1){
34028                 this.resizeTo(this.config.initialSize);
34029             }
34030         }
34031         this.fireEvent("paneladded", this, panel);
34032         return panel;
34033     },
34034     
34035     /**
34036      * Returns true if the panel is in this region.
34037      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34038      * @return {Boolean}
34039      */
34040     hasPanel : function(panel){
34041         if(typeof panel == "object"){ // must be panel obj
34042             panel = panel.getId();
34043         }
34044         return this.getPanel(panel) ? true : false;
34045     },
34046     
34047     /**
34048      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34049      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34050      * @param {Boolean} preservePanel Overrides the config preservePanel option
34051      * @return {Roo.ContentPanel} The panel that was removed
34052      */
34053     remove : function(panel, preservePanel){
34054         panel = this.getPanel(panel);
34055         if(!panel){
34056             return null;
34057         }
34058         var e = {};
34059         this.fireEvent("beforeremove", this, panel, e);
34060         if(e.cancel === true){
34061             return null;
34062         }
34063         var panelId = panel.getId();
34064         this.panels.removeKey(panelId);
34065         return panel;
34066     },
34067     
34068     /**
34069      * Returns the panel specified or null if it's not in this region.
34070      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34071      * @return {Roo.ContentPanel}
34072      */
34073     getPanel : function(id){
34074         if(typeof id == "object"){ // must be panel obj
34075             return id;
34076         }
34077         return this.panels.get(id);
34078     },
34079     
34080     /**
34081      * Returns this regions position (north/south/east/west/center).
34082      * @return {String} 
34083      */
34084     getPosition: function(){
34085         return this.position;    
34086     }
34087 });/*
34088  * Based on:
34089  * Ext JS Library 1.1.1
34090  * Copyright(c) 2006-2007, Ext JS, LLC.
34091  *
34092  * Originally Released Under LGPL - original licence link has changed is not relivant.
34093  *
34094  * Fork - LGPL
34095  * <script type="text/javascript">
34096  */
34097  
34098 /**
34099  * @class Roo.bootstrap.layout.Region
34100  * @extends Roo.bootstrap.layout.Basic
34101  * This class represents a region in a layout manager.
34102  
34103  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34104  * @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})
34105  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
34106  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
34107  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
34108  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
34109  * @cfg {String}    title           The title for the region (overrides panel titles)
34110  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
34111  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34112  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
34113  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34114  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
34115  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34116  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
34117  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
34118  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
34119  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
34120
34121  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
34122  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
34123  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
34124  * @cfg {Number}    width           For East/West panels
34125  * @cfg {Number}    height          For North/South panels
34126  * @cfg {Boolean}   split           To show the splitter
34127  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
34128  * 
34129  * @cfg {string}   cls             Extra CSS classes to add to region
34130  * 
34131  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34132  * @cfg {string}   region  the region that it inhabits..
34133  *
34134
34135  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
34136  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
34137
34138  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
34139  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
34140  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
34141  */
34142 Roo.bootstrap.layout.Region = function(config)
34143 {
34144     this.applyConfig(config);
34145
34146     var mgr = config.mgr;
34147     var pos = config.region;
34148     config.skipConfig = true;
34149     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
34150     
34151     if (mgr.el) {
34152         this.onRender(mgr.el);   
34153     }
34154      
34155     this.visible = true;
34156     this.collapsed = false;
34157     this.unrendered_panels = [];
34158 };
34159
34160 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
34161
34162     position: '', // set by wrapper (eg. north/south etc..)
34163     unrendered_panels : null,  // unrendered panels.
34164     createBody : function(){
34165         /** This region's body element 
34166         * @type Roo.Element */
34167         this.bodyEl = this.el.createChild({
34168                 tag: "div",
34169                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
34170         });
34171     },
34172
34173     onRender: function(ctr, pos)
34174     {
34175         var dh = Roo.DomHelper;
34176         /** This region's container element 
34177         * @type Roo.Element */
34178         this.el = dh.append(ctr.dom, {
34179                 tag: "div",
34180                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
34181             }, true);
34182         /** This region's title element 
34183         * @type Roo.Element */
34184     
34185         this.titleEl = dh.append(this.el.dom,
34186             {
34187                     tag: "div",
34188                     unselectable: "on",
34189                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
34190                     children:[
34191                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
34192                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
34193                     ]}, true);
34194         
34195         this.titleEl.enableDisplayMode();
34196         /** This region's title text element 
34197         * @type HTMLElement */
34198         this.titleTextEl = this.titleEl.dom.firstChild;
34199         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
34200         /*
34201         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
34202         this.closeBtn.enableDisplayMode();
34203         this.closeBtn.on("click", this.closeClicked, this);
34204         this.closeBtn.hide();
34205     */
34206         this.createBody(this.config);
34207         if(this.config.hideWhenEmpty){
34208             this.hide();
34209             this.on("paneladded", this.validateVisibility, this);
34210             this.on("panelremoved", this.validateVisibility, this);
34211         }
34212         if(this.autoScroll){
34213             this.bodyEl.setStyle("overflow", "auto");
34214         }else{
34215             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
34216         }
34217         //if(c.titlebar !== false){
34218             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
34219                 this.titleEl.hide();
34220             }else{
34221                 this.titleEl.show();
34222                 if(this.config.title){
34223                     this.titleTextEl.innerHTML = this.config.title;
34224                 }
34225             }
34226         //}
34227         if(this.config.collapsed){
34228             this.collapse(true);
34229         }
34230         if(this.config.hidden){
34231             this.hide();
34232         }
34233         
34234         if (this.unrendered_panels && this.unrendered_panels.length) {
34235             for (var i =0;i< this.unrendered_panels.length; i++) {
34236                 this.add(this.unrendered_panels[i]);
34237             }
34238             this.unrendered_panels = null;
34239             
34240         }
34241         
34242     },
34243     
34244     applyConfig : function(c)
34245     {
34246         /*
34247          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
34248             var dh = Roo.DomHelper;
34249             if(c.titlebar !== false){
34250                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
34251                 this.collapseBtn.on("click", this.collapse, this);
34252                 this.collapseBtn.enableDisplayMode();
34253                 /*
34254                 if(c.showPin === true || this.showPin){
34255                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
34256                     this.stickBtn.enableDisplayMode();
34257                     this.stickBtn.on("click", this.expand, this);
34258                     this.stickBtn.hide();
34259                 }
34260                 
34261             }
34262             */
34263             /** This region's collapsed element
34264             * @type Roo.Element */
34265             /*
34266              *
34267             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
34268                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
34269             ]}, true);
34270             
34271             if(c.floatable !== false){
34272                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
34273                this.collapsedEl.on("click", this.collapseClick, this);
34274             }
34275
34276             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
34277                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
34278                    id: "message", unselectable: "on", style:{"float":"left"}});
34279                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
34280              }
34281             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
34282             this.expandBtn.on("click", this.expand, this);
34283             
34284         }
34285         
34286         if(this.collapseBtn){
34287             this.collapseBtn.setVisible(c.collapsible == true);
34288         }
34289         
34290         this.cmargins = c.cmargins || this.cmargins ||
34291                          (this.position == "west" || this.position == "east" ?
34292                              {top: 0, left: 2, right:2, bottom: 0} :
34293                              {top: 2, left: 0, right:0, bottom: 2});
34294         */
34295         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34296         
34297         
34298         this.bottomTabs = c.tabPosition != "top";
34299         
34300         this.autoScroll = c.autoScroll || false;
34301         
34302         
34303        
34304         
34305         this.duration = c.duration || .30;
34306         this.slideDuration = c.slideDuration || .45;
34307         this.config = c;
34308        
34309     },
34310     /**
34311      * Returns true if this region is currently visible.
34312      * @return {Boolean}
34313      */
34314     isVisible : function(){
34315         return this.visible;
34316     },
34317
34318     /**
34319      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
34320      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
34321      */
34322     //setCollapsedTitle : function(title){
34323     //    title = title || "&#160;";
34324      //   if(this.collapsedTitleTextEl){
34325       //      this.collapsedTitleTextEl.innerHTML = title;
34326        // }
34327     //},
34328
34329     getBox : function(){
34330         var b;
34331       //  if(!this.collapsed){
34332             b = this.el.getBox(false, true);
34333        // }else{
34334           //  b = this.collapsedEl.getBox(false, true);
34335         //}
34336         return b;
34337     },
34338
34339     getMargins : function(){
34340         return this.margins;
34341         //return this.collapsed ? this.cmargins : this.margins;
34342     },
34343 /*
34344     highlight : function(){
34345         this.el.addClass("x-layout-panel-dragover");
34346     },
34347
34348     unhighlight : function(){
34349         this.el.removeClass("x-layout-panel-dragover");
34350     },
34351 */
34352     updateBox : function(box)
34353     {
34354         if (!this.bodyEl) {
34355             return; // not rendered yet..
34356         }
34357         
34358         this.box = box;
34359         if(!this.collapsed){
34360             this.el.dom.style.left = box.x + "px";
34361             this.el.dom.style.top = box.y + "px";
34362             this.updateBody(box.width, box.height);
34363         }else{
34364             this.collapsedEl.dom.style.left = box.x + "px";
34365             this.collapsedEl.dom.style.top = box.y + "px";
34366             this.collapsedEl.setSize(box.width, box.height);
34367         }
34368         if(this.tabs){
34369             this.tabs.autoSizeTabs();
34370         }
34371     },
34372
34373     updateBody : function(w, h)
34374     {
34375         if(w !== null){
34376             this.el.setWidth(w);
34377             w -= this.el.getBorderWidth("rl");
34378             if(this.config.adjustments){
34379                 w += this.config.adjustments[0];
34380             }
34381         }
34382         if(h !== null && h > 0){
34383             this.el.setHeight(h);
34384             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
34385             h -= this.el.getBorderWidth("tb");
34386             if(this.config.adjustments){
34387                 h += this.config.adjustments[1];
34388             }
34389             this.bodyEl.setHeight(h);
34390             if(this.tabs){
34391                 h = this.tabs.syncHeight(h);
34392             }
34393         }
34394         if(this.panelSize){
34395             w = w !== null ? w : this.panelSize.width;
34396             h = h !== null ? h : this.panelSize.height;
34397         }
34398         if(this.activePanel){
34399             var el = this.activePanel.getEl();
34400             w = w !== null ? w : el.getWidth();
34401             h = h !== null ? h : el.getHeight();
34402             this.panelSize = {width: w, height: h};
34403             this.activePanel.setSize(w, h);
34404         }
34405         if(Roo.isIE && this.tabs){
34406             this.tabs.el.repaint();
34407         }
34408     },
34409
34410     /**
34411      * Returns the container element for this region.
34412      * @return {Roo.Element}
34413      */
34414     getEl : function(){
34415         return this.el;
34416     },
34417
34418     /**
34419      * Hides this region.
34420      */
34421     hide : function(){
34422         //if(!this.collapsed){
34423             this.el.dom.style.left = "-2000px";
34424             this.el.hide();
34425         //}else{
34426          //   this.collapsedEl.dom.style.left = "-2000px";
34427          //   this.collapsedEl.hide();
34428        // }
34429         this.visible = false;
34430         this.fireEvent("visibilitychange", this, false);
34431     },
34432
34433     /**
34434      * Shows this region if it was previously hidden.
34435      */
34436     show : function(){
34437         //if(!this.collapsed){
34438             this.el.show();
34439         //}else{
34440         //    this.collapsedEl.show();
34441        // }
34442         this.visible = true;
34443         this.fireEvent("visibilitychange", this, true);
34444     },
34445 /*
34446     closeClicked : function(){
34447         if(this.activePanel){
34448             this.remove(this.activePanel);
34449         }
34450     },
34451
34452     collapseClick : function(e){
34453         if(this.isSlid){
34454            e.stopPropagation();
34455            this.slideIn();
34456         }else{
34457            e.stopPropagation();
34458            this.slideOut();
34459         }
34460     },
34461 */
34462     /**
34463      * Collapses this region.
34464      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
34465      */
34466     /*
34467     collapse : function(skipAnim, skipCheck = false){
34468         if(this.collapsed) {
34469             return;
34470         }
34471         
34472         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
34473             
34474             this.collapsed = true;
34475             if(this.split){
34476                 this.split.el.hide();
34477             }
34478             if(this.config.animate && skipAnim !== true){
34479                 this.fireEvent("invalidated", this);
34480                 this.animateCollapse();
34481             }else{
34482                 this.el.setLocation(-20000,-20000);
34483                 this.el.hide();
34484                 this.collapsedEl.show();
34485                 this.fireEvent("collapsed", this);
34486                 this.fireEvent("invalidated", this);
34487             }
34488         }
34489         
34490     },
34491 */
34492     animateCollapse : function(){
34493         // overridden
34494     },
34495
34496     /**
34497      * Expands this region if it was previously collapsed.
34498      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
34499      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
34500      */
34501     /*
34502     expand : function(e, skipAnim){
34503         if(e) {
34504             e.stopPropagation();
34505         }
34506         if(!this.collapsed || this.el.hasActiveFx()) {
34507             return;
34508         }
34509         if(this.isSlid){
34510             this.afterSlideIn();
34511             skipAnim = true;
34512         }
34513         this.collapsed = false;
34514         if(this.config.animate && skipAnim !== true){
34515             this.animateExpand();
34516         }else{
34517             this.el.show();
34518             if(this.split){
34519                 this.split.el.show();
34520             }
34521             this.collapsedEl.setLocation(-2000,-2000);
34522             this.collapsedEl.hide();
34523             this.fireEvent("invalidated", this);
34524             this.fireEvent("expanded", this);
34525         }
34526     },
34527 */
34528     animateExpand : function(){
34529         // overridden
34530     },
34531
34532     initTabs : function()
34533     {
34534         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
34535         
34536         var ts = new Roo.bootstrap.panel.Tabs({
34537                 el: this.bodyEl.dom,
34538                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
34539                 disableTooltips: this.config.disableTabTips,
34540                 toolbar : this.config.toolbar
34541             });
34542         
34543         if(this.config.hideTabs){
34544             ts.stripWrap.setDisplayed(false);
34545         }
34546         this.tabs = ts;
34547         ts.resizeTabs = this.config.resizeTabs === true;
34548         ts.minTabWidth = this.config.minTabWidth || 40;
34549         ts.maxTabWidth = this.config.maxTabWidth || 250;
34550         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
34551         ts.monitorResize = false;
34552         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
34553         ts.bodyEl.addClass('roo-layout-tabs-body');
34554         this.panels.each(this.initPanelAsTab, this);
34555     },
34556
34557     initPanelAsTab : function(panel){
34558         var ti = this.tabs.addTab(
34559             panel.getEl().id,
34560             panel.getTitle(),
34561             null,
34562             this.config.closeOnTab && panel.isClosable(),
34563             panel.tpl
34564         );
34565         if(panel.tabTip !== undefined){
34566             ti.setTooltip(panel.tabTip);
34567         }
34568         ti.on("activate", function(){
34569               this.setActivePanel(panel);
34570         }, this);
34571         
34572         if(this.config.closeOnTab){
34573             ti.on("beforeclose", function(t, e){
34574                 e.cancel = true;
34575                 this.remove(panel);
34576             }, this);
34577         }
34578         
34579         panel.tabItem = ti;
34580         
34581         return ti;
34582     },
34583
34584     updatePanelTitle : function(panel, title)
34585     {
34586         if(this.activePanel == panel){
34587             this.updateTitle(title);
34588         }
34589         if(this.tabs){
34590             var ti = this.tabs.getTab(panel.getEl().id);
34591             ti.setText(title);
34592             if(panel.tabTip !== undefined){
34593                 ti.setTooltip(panel.tabTip);
34594             }
34595         }
34596     },
34597
34598     updateTitle : function(title){
34599         if(this.titleTextEl && !this.config.title){
34600             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
34601         }
34602     },
34603
34604     setActivePanel : function(panel)
34605     {
34606         panel = this.getPanel(panel);
34607         if(this.activePanel && this.activePanel != panel){
34608             this.activePanel.setActiveState(false);
34609         }
34610         this.activePanel = panel;
34611         panel.setActiveState(true);
34612         if(this.panelSize){
34613             panel.setSize(this.panelSize.width, this.panelSize.height);
34614         }
34615         if(this.closeBtn){
34616             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
34617         }
34618         this.updateTitle(panel.getTitle());
34619         if(this.tabs){
34620             this.fireEvent("invalidated", this);
34621         }
34622         this.fireEvent("panelactivated", this, panel);
34623     },
34624
34625     /**
34626      * Shows the specified panel.
34627      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34628      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34629      */
34630     showPanel : function(panel)
34631     {
34632         panel = this.getPanel(panel);
34633         if(panel){
34634             if(this.tabs){
34635                 var tab = this.tabs.getTab(panel.getEl().id);
34636                 if(tab.isHidden()){
34637                     this.tabs.unhideTab(tab.id);
34638                 }
34639                 tab.activate();
34640             }else{
34641                 this.setActivePanel(panel);
34642             }
34643         }
34644         return panel;
34645     },
34646
34647     /**
34648      * Get the active panel for this region.
34649      * @return {Roo.ContentPanel} The active panel or null
34650      */
34651     getActivePanel : function(){
34652         return this.activePanel;
34653     },
34654
34655     validateVisibility : function(){
34656         if(this.panels.getCount() < 1){
34657             this.updateTitle("&#160;");
34658             this.closeBtn.hide();
34659             this.hide();
34660         }else{
34661             if(!this.isVisible()){
34662                 this.show();
34663             }
34664         }
34665     },
34666
34667     /**
34668      * Adds the passed ContentPanel(s) to this region.
34669      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34670      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34671      */
34672     add : function(panel)
34673     {
34674         if(arguments.length > 1){
34675             for(var i = 0, len = arguments.length; i < len; i++) {
34676                 this.add(arguments[i]);
34677             }
34678             return null;
34679         }
34680         
34681         // if we have not been rendered yet, then we can not really do much of this..
34682         if (!this.bodyEl) {
34683             this.unrendered_panels.push(panel);
34684             return panel;
34685         }
34686         
34687         
34688         
34689         
34690         if(this.hasPanel(panel)){
34691             this.showPanel(panel);
34692             return panel;
34693         }
34694         panel.setRegion(this);
34695         this.panels.add(panel);
34696        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34697             // sinle panel - no tab...?? would it not be better to render it with the tabs,
34698             // and hide them... ???
34699             this.bodyEl.dom.appendChild(panel.getEl().dom);
34700             if(panel.background !== true){
34701                 this.setActivePanel(panel);
34702             }
34703             this.fireEvent("paneladded", this, panel);
34704             return panel;
34705         }
34706         */
34707         if(!this.tabs){
34708             this.initTabs();
34709         }else{
34710             this.initPanelAsTab(panel);
34711         }
34712         
34713         
34714         if(panel.background !== true){
34715             this.tabs.activate(panel.getEl().id);
34716         }
34717         this.fireEvent("paneladded", this, panel);
34718         return panel;
34719     },
34720
34721     /**
34722      * Hides the tab for the specified panel.
34723      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34724      */
34725     hidePanel : function(panel){
34726         if(this.tabs && (panel = this.getPanel(panel))){
34727             this.tabs.hideTab(panel.getEl().id);
34728         }
34729     },
34730
34731     /**
34732      * Unhides the tab for a previously hidden panel.
34733      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34734      */
34735     unhidePanel : function(panel){
34736         if(this.tabs && (panel = this.getPanel(panel))){
34737             this.tabs.unhideTab(panel.getEl().id);
34738         }
34739     },
34740
34741     clearPanels : function(){
34742         while(this.panels.getCount() > 0){
34743              this.remove(this.panels.first());
34744         }
34745     },
34746
34747     /**
34748      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34749      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34750      * @param {Boolean} preservePanel Overrides the config preservePanel option
34751      * @return {Roo.ContentPanel} The panel that was removed
34752      */
34753     remove : function(panel, preservePanel)
34754     {
34755         panel = this.getPanel(panel);
34756         if(!panel){
34757             return null;
34758         }
34759         var e = {};
34760         this.fireEvent("beforeremove", this, panel, e);
34761         if(e.cancel === true){
34762             return null;
34763         }
34764         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34765         var panelId = panel.getId();
34766         this.panels.removeKey(panelId);
34767         if(preservePanel){
34768             document.body.appendChild(panel.getEl().dom);
34769         }
34770         if(this.tabs){
34771             this.tabs.removeTab(panel.getEl().id);
34772         }else if (!preservePanel){
34773             this.bodyEl.dom.removeChild(panel.getEl().dom);
34774         }
34775         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
34776             var p = this.panels.first();
34777             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
34778             tempEl.appendChild(p.getEl().dom);
34779             this.bodyEl.update("");
34780             this.bodyEl.dom.appendChild(p.getEl().dom);
34781             tempEl = null;
34782             this.updateTitle(p.getTitle());
34783             this.tabs = null;
34784             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34785             this.setActivePanel(p);
34786         }
34787         panel.setRegion(null);
34788         if(this.activePanel == panel){
34789             this.activePanel = null;
34790         }
34791         if(this.config.autoDestroy !== false && preservePanel !== true){
34792             try{panel.destroy();}catch(e){}
34793         }
34794         this.fireEvent("panelremoved", this, panel);
34795         return panel;
34796     },
34797
34798     /**
34799      * Returns the TabPanel component used by this region
34800      * @return {Roo.TabPanel}
34801      */
34802     getTabs : function(){
34803         return this.tabs;
34804     },
34805
34806     createTool : function(parentEl, className){
34807         var btn = Roo.DomHelper.append(parentEl, {
34808             tag: "div",
34809             cls: "x-layout-tools-button",
34810             children: [ {
34811                 tag: "div",
34812                 cls: "roo-layout-tools-button-inner " + className,
34813                 html: "&#160;"
34814             }]
34815         }, true);
34816         btn.addClassOnOver("roo-layout-tools-button-over");
34817         return btn;
34818     }
34819 });/*
34820  * Based on:
34821  * Ext JS Library 1.1.1
34822  * Copyright(c) 2006-2007, Ext JS, LLC.
34823  *
34824  * Originally Released Under LGPL - original licence link has changed is not relivant.
34825  *
34826  * Fork - LGPL
34827  * <script type="text/javascript">
34828  */
34829  
34830
34831
34832 /**
34833  * @class Roo.SplitLayoutRegion
34834  * @extends Roo.LayoutRegion
34835  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34836  */
34837 Roo.bootstrap.layout.Split = function(config){
34838     this.cursor = config.cursor;
34839     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
34840 };
34841
34842 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
34843 {
34844     splitTip : "Drag to resize.",
34845     collapsibleSplitTip : "Drag to resize. Double click to hide.",
34846     useSplitTips : false,
34847
34848     applyConfig : function(config){
34849         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
34850     },
34851     
34852     onRender : function(ctr,pos) {
34853         
34854         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
34855         if(!this.config.split){
34856             return;
34857         }
34858         if(!this.split){
34859             
34860             var splitEl = Roo.DomHelper.append(ctr.dom,  {
34861                             tag: "div",
34862                             id: this.el.id + "-split",
34863                             cls: "roo-layout-split roo-layout-split-"+this.position,
34864                             html: "&#160;"
34865             });
34866             /** The SplitBar for this region 
34867             * @type Roo.SplitBar */
34868             // does not exist yet...
34869             Roo.log([this.position, this.orientation]);
34870             
34871             this.split = new Roo.bootstrap.SplitBar({
34872                 dragElement : splitEl,
34873                 resizingElement: this.el,
34874                 orientation : this.orientation
34875             });
34876             
34877             this.split.on("moved", this.onSplitMove, this);
34878             this.split.useShim = this.config.useShim === true;
34879             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34880             if(this.useSplitTips){
34881                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34882             }
34883             //if(config.collapsible){
34884             //    this.split.el.on("dblclick", this.collapse,  this);
34885             //}
34886         }
34887         if(typeof this.config.minSize != "undefined"){
34888             this.split.minSize = this.config.minSize;
34889         }
34890         if(typeof this.config.maxSize != "undefined"){
34891             this.split.maxSize = this.config.maxSize;
34892         }
34893         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
34894             this.hideSplitter();
34895         }
34896         
34897     },
34898
34899     getHMaxSize : function(){
34900          var cmax = this.config.maxSize || 10000;
34901          var center = this.mgr.getRegion("center");
34902          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34903     },
34904
34905     getVMaxSize : function(){
34906          var cmax = this.config.maxSize || 10000;
34907          var center = this.mgr.getRegion("center");
34908          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34909     },
34910
34911     onSplitMove : function(split, newSize){
34912         this.fireEvent("resized", this, newSize);
34913     },
34914     
34915     /** 
34916      * Returns the {@link Roo.SplitBar} for this region.
34917      * @return {Roo.SplitBar}
34918      */
34919     getSplitBar : function(){
34920         return this.split;
34921     },
34922     
34923     hide : function(){
34924         this.hideSplitter();
34925         Roo.bootstrap.layout.Split.superclass.hide.call(this);
34926     },
34927
34928     hideSplitter : function(){
34929         if(this.split){
34930             this.split.el.setLocation(-2000,-2000);
34931             this.split.el.hide();
34932         }
34933     },
34934
34935     show : function(){
34936         if(this.split){
34937             this.split.el.show();
34938         }
34939         Roo.bootstrap.layout.Split.superclass.show.call(this);
34940     },
34941     
34942     beforeSlide: function(){
34943         if(Roo.isGecko){// firefox overflow auto bug workaround
34944             this.bodyEl.clip();
34945             if(this.tabs) {
34946                 this.tabs.bodyEl.clip();
34947             }
34948             if(this.activePanel){
34949                 this.activePanel.getEl().clip();
34950                 
34951                 if(this.activePanel.beforeSlide){
34952                     this.activePanel.beforeSlide();
34953                 }
34954             }
34955         }
34956     },
34957     
34958     afterSlide : function(){
34959         if(Roo.isGecko){// firefox overflow auto bug workaround
34960             this.bodyEl.unclip();
34961             if(this.tabs) {
34962                 this.tabs.bodyEl.unclip();
34963             }
34964             if(this.activePanel){
34965                 this.activePanel.getEl().unclip();
34966                 if(this.activePanel.afterSlide){
34967                     this.activePanel.afterSlide();
34968                 }
34969             }
34970         }
34971     },
34972
34973     initAutoHide : function(){
34974         if(this.autoHide !== false){
34975             if(!this.autoHideHd){
34976                 var st = new Roo.util.DelayedTask(this.slideIn, this);
34977                 this.autoHideHd = {
34978                     "mouseout": function(e){
34979                         if(!e.within(this.el, true)){
34980                             st.delay(500);
34981                         }
34982                     },
34983                     "mouseover" : function(e){
34984                         st.cancel();
34985                     },
34986                     scope : this
34987                 };
34988             }
34989             this.el.on(this.autoHideHd);
34990         }
34991     },
34992
34993     clearAutoHide : function(){
34994         if(this.autoHide !== false){
34995             this.el.un("mouseout", this.autoHideHd.mouseout);
34996             this.el.un("mouseover", this.autoHideHd.mouseover);
34997         }
34998     },
34999
35000     clearMonitor : function(){
35001         Roo.get(document).un("click", this.slideInIf, this);
35002     },
35003
35004     // these names are backwards but not changed for compat
35005     slideOut : function(){
35006         if(this.isSlid || this.el.hasActiveFx()){
35007             return;
35008         }
35009         this.isSlid = true;
35010         if(this.collapseBtn){
35011             this.collapseBtn.hide();
35012         }
35013         this.closeBtnState = this.closeBtn.getStyle('display');
35014         this.closeBtn.hide();
35015         if(this.stickBtn){
35016             this.stickBtn.show();
35017         }
35018         this.el.show();
35019         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35020         this.beforeSlide();
35021         this.el.setStyle("z-index", 10001);
35022         this.el.slideIn(this.getSlideAnchor(), {
35023             callback: function(){
35024                 this.afterSlide();
35025                 this.initAutoHide();
35026                 Roo.get(document).on("click", this.slideInIf, this);
35027                 this.fireEvent("slideshow", this);
35028             },
35029             scope: this,
35030             block: true
35031         });
35032     },
35033
35034     afterSlideIn : function(){
35035         this.clearAutoHide();
35036         this.isSlid = false;
35037         this.clearMonitor();
35038         this.el.setStyle("z-index", "");
35039         if(this.collapseBtn){
35040             this.collapseBtn.show();
35041         }
35042         this.closeBtn.setStyle('display', this.closeBtnState);
35043         if(this.stickBtn){
35044             this.stickBtn.hide();
35045         }
35046         this.fireEvent("slidehide", this);
35047     },
35048
35049     slideIn : function(cb){
35050         if(!this.isSlid || this.el.hasActiveFx()){
35051             Roo.callback(cb);
35052             return;
35053         }
35054         this.isSlid = false;
35055         this.beforeSlide();
35056         this.el.slideOut(this.getSlideAnchor(), {
35057             callback: function(){
35058                 this.el.setLeftTop(-10000, -10000);
35059                 this.afterSlide();
35060                 this.afterSlideIn();
35061                 Roo.callback(cb);
35062             },
35063             scope: this,
35064             block: true
35065         });
35066     },
35067     
35068     slideInIf : function(e){
35069         if(!e.within(this.el)){
35070             this.slideIn();
35071         }
35072     },
35073
35074     animateCollapse : function(){
35075         this.beforeSlide();
35076         this.el.setStyle("z-index", 20000);
35077         var anchor = this.getSlideAnchor();
35078         this.el.slideOut(anchor, {
35079             callback : function(){
35080                 this.el.setStyle("z-index", "");
35081                 this.collapsedEl.slideIn(anchor, {duration:.3});
35082                 this.afterSlide();
35083                 this.el.setLocation(-10000,-10000);
35084                 this.el.hide();
35085                 this.fireEvent("collapsed", this);
35086             },
35087             scope: this,
35088             block: true
35089         });
35090     },
35091
35092     animateExpand : function(){
35093         this.beforeSlide();
35094         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35095         this.el.setStyle("z-index", 20000);
35096         this.collapsedEl.hide({
35097             duration:.1
35098         });
35099         this.el.slideIn(this.getSlideAnchor(), {
35100             callback : function(){
35101                 this.el.setStyle("z-index", "");
35102                 this.afterSlide();
35103                 if(this.split){
35104                     this.split.el.show();
35105                 }
35106                 this.fireEvent("invalidated", this);
35107                 this.fireEvent("expanded", this);
35108             },
35109             scope: this,
35110             block: true
35111         });
35112     },
35113
35114     anchors : {
35115         "west" : "left",
35116         "east" : "right",
35117         "north" : "top",
35118         "south" : "bottom"
35119     },
35120
35121     sanchors : {
35122         "west" : "l",
35123         "east" : "r",
35124         "north" : "t",
35125         "south" : "b"
35126     },
35127
35128     canchors : {
35129         "west" : "tl-tr",
35130         "east" : "tr-tl",
35131         "north" : "tl-bl",
35132         "south" : "bl-tl"
35133     },
35134
35135     getAnchor : function(){
35136         return this.anchors[this.position];
35137     },
35138
35139     getCollapseAnchor : function(){
35140         return this.canchors[this.position];
35141     },
35142
35143     getSlideAnchor : function(){
35144         return this.sanchors[this.position];
35145     },
35146
35147     getAlignAdj : function(){
35148         var cm = this.cmargins;
35149         switch(this.position){
35150             case "west":
35151                 return [0, 0];
35152             break;
35153             case "east":
35154                 return [0, 0];
35155             break;
35156             case "north":
35157                 return [0, 0];
35158             break;
35159             case "south":
35160                 return [0, 0];
35161             break;
35162         }
35163     },
35164
35165     getExpandAdj : function(){
35166         var c = this.collapsedEl, cm = this.cmargins;
35167         switch(this.position){
35168             case "west":
35169                 return [-(cm.right+c.getWidth()+cm.left), 0];
35170             break;
35171             case "east":
35172                 return [cm.right+c.getWidth()+cm.left, 0];
35173             break;
35174             case "north":
35175                 return [0, -(cm.top+cm.bottom+c.getHeight())];
35176             break;
35177             case "south":
35178                 return [0, cm.top+cm.bottom+c.getHeight()];
35179             break;
35180         }
35181     }
35182 });/*
35183  * Based on:
35184  * Ext JS Library 1.1.1
35185  * Copyright(c) 2006-2007, Ext JS, LLC.
35186  *
35187  * Originally Released Under LGPL - original licence link has changed is not relivant.
35188  *
35189  * Fork - LGPL
35190  * <script type="text/javascript">
35191  */
35192 /*
35193  * These classes are private internal classes
35194  */
35195 Roo.bootstrap.layout.Center = function(config){
35196     config.region = "center";
35197     Roo.bootstrap.layout.Region.call(this, config);
35198     this.visible = true;
35199     this.minWidth = config.minWidth || 20;
35200     this.minHeight = config.minHeight || 20;
35201 };
35202
35203 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
35204     hide : function(){
35205         // center panel can't be hidden
35206     },
35207     
35208     show : function(){
35209         // center panel can't be hidden
35210     },
35211     
35212     getMinWidth: function(){
35213         return this.minWidth;
35214     },
35215     
35216     getMinHeight: function(){
35217         return this.minHeight;
35218     }
35219 });
35220
35221
35222
35223
35224  
35225
35226
35227
35228
35229
35230 Roo.bootstrap.layout.North = function(config)
35231 {
35232     config.region = 'north';
35233     config.cursor = 'n-resize';
35234     
35235     Roo.bootstrap.layout.Split.call(this, config);
35236     
35237     
35238     if(this.split){
35239         this.split.placement = Roo.bootstrap.SplitBar.TOP;
35240         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
35241         this.split.el.addClass("roo-layout-split-v");
35242     }
35243     var size = config.initialSize || config.height;
35244     if(typeof size != "undefined"){
35245         this.el.setHeight(size);
35246     }
35247 };
35248 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
35249 {
35250     orientation: Roo.bootstrap.SplitBar.VERTICAL,
35251     
35252     
35253     
35254     getBox : function(){
35255         if(this.collapsed){
35256             return this.collapsedEl.getBox();
35257         }
35258         var box = this.el.getBox();
35259         if(this.split){
35260             box.height += this.split.el.getHeight();
35261         }
35262         return box;
35263     },
35264     
35265     updateBox : function(box){
35266         if(this.split && !this.collapsed){
35267             box.height -= this.split.el.getHeight();
35268             this.split.el.setLeft(box.x);
35269             this.split.el.setTop(box.y+box.height);
35270             this.split.el.setWidth(box.width);
35271         }
35272         if(this.collapsed){
35273             this.updateBody(box.width, null);
35274         }
35275         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35276     }
35277 });
35278
35279
35280
35281
35282
35283 Roo.bootstrap.layout.South = function(config){
35284     config.region = 'south';
35285     config.cursor = 's-resize';
35286     Roo.bootstrap.layout.Split.call(this, config);
35287     if(this.split){
35288         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
35289         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
35290         this.split.el.addClass("roo-layout-split-v");
35291     }
35292     var size = config.initialSize || config.height;
35293     if(typeof size != "undefined"){
35294         this.el.setHeight(size);
35295     }
35296 };
35297
35298 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
35299     orientation: Roo.bootstrap.SplitBar.VERTICAL,
35300     getBox : function(){
35301         if(this.collapsed){
35302             return this.collapsedEl.getBox();
35303         }
35304         var box = this.el.getBox();
35305         if(this.split){
35306             var sh = this.split.el.getHeight();
35307             box.height += sh;
35308             box.y -= sh;
35309         }
35310         return box;
35311     },
35312     
35313     updateBox : function(box){
35314         if(this.split && !this.collapsed){
35315             var sh = this.split.el.getHeight();
35316             box.height -= sh;
35317             box.y += sh;
35318             this.split.el.setLeft(box.x);
35319             this.split.el.setTop(box.y-sh);
35320             this.split.el.setWidth(box.width);
35321         }
35322         if(this.collapsed){
35323             this.updateBody(box.width, null);
35324         }
35325         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35326     }
35327 });
35328
35329 Roo.bootstrap.layout.East = function(config){
35330     config.region = "east";
35331     config.cursor = "e-resize";
35332     Roo.bootstrap.layout.Split.call(this, config);
35333     if(this.split){
35334         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
35335         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
35336         this.split.el.addClass("roo-layout-split-h");
35337     }
35338     var size = config.initialSize || config.width;
35339     if(typeof size != "undefined"){
35340         this.el.setWidth(size);
35341     }
35342 };
35343 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
35344     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
35345     getBox : function(){
35346         if(this.collapsed){
35347             return this.collapsedEl.getBox();
35348         }
35349         var box = this.el.getBox();
35350         if(this.split){
35351             var sw = this.split.el.getWidth();
35352             box.width += sw;
35353             box.x -= sw;
35354         }
35355         return box;
35356     },
35357
35358     updateBox : function(box){
35359         if(this.split && !this.collapsed){
35360             var sw = this.split.el.getWidth();
35361             box.width -= sw;
35362             this.split.el.setLeft(box.x);
35363             this.split.el.setTop(box.y);
35364             this.split.el.setHeight(box.height);
35365             box.x += sw;
35366         }
35367         if(this.collapsed){
35368             this.updateBody(null, box.height);
35369         }
35370         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35371     }
35372 });
35373
35374 Roo.bootstrap.layout.West = function(config){
35375     config.region = "west";
35376     config.cursor = "w-resize";
35377     
35378     Roo.bootstrap.layout.Split.call(this, config);
35379     if(this.split){
35380         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
35381         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
35382         this.split.el.addClass("roo-layout-split-h");
35383     }
35384     
35385 };
35386 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
35387     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
35388     
35389     onRender: function(ctr, pos)
35390     {
35391         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
35392         var size = this.config.initialSize || this.config.width;
35393         if(typeof size != "undefined"){
35394             this.el.setWidth(size);
35395         }
35396     },
35397     
35398     getBox : function(){
35399         if(this.collapsed){
35400             return this.collapsedEl.getBox();
35401         }
35402         var box = this.el.getBox();
35403         if(this.split){
35404             box.width += this.split.el.getWidth();
35405         }
35406         return box;
35407     },
35408     
35409     updateBox : function(box){
35410         if(this.split && !this.collapsed){
35411             var sw = this.split.el.getWidth();
35412             box.width -= sw;
35413             this.split.el.setLeft(box.x+box.width);
35414             this.split.el.setTop(box.y);
35415             this.split.el.setHeight(box.height);
35416         }
35417         if(this.collapsed){
35418             this.updateBody(null, box.height);
35419         }
35420         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35421     }
35422 });
35423 Roo.namespace("Roo.bootstrap.panel");/*
35424  * Based on:
35425  * Ext JS Library 1.1.1
35426  * Copyright(c) 2006-2007, Ext JS, LLC.
35427  *
35428  * Originally Released Under LGPL - original licence link has changed is not relivant.
35429  *
35430  * Fork - LGPL
35431  * <script type="text/javascript">
35432  */
35433 /**
35434  * @class Roo.ContentPanel
35435  * @extends Roo.util.Observable
35436  * A basic ContentPanel element.
35437  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
35438  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
35439  * @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
35440  * @cfg {Boolean}   closable      True if the panel can be closed/removed
35441  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
35442  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
35443  * @cfg {Toolbar}   toolbar       A toolbar for this panel
35444  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
35445  * @cfg {String} title          The title for this panel
35446  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
35447  * @cfg {String} url            Calls {@link #setUrl} with this value
35448  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
35449  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
35450  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
35451  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
35452  * @cfg {Boolean} badges render the badges
35453
35454  * @constructor
35455  * Create a new ContentPanel.
35456  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
35457  * @param {String/Object} config A string to set only the title or a config object
35458  * @param {String} content (optional) Set the HTML content for this panel
35459  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
35460  */
35461 Roo.bootstrap.panel.Content = function( config){
35462     
35463     this.tpl = config.tpl || false;
35464     
35465     var el = config.el;
35466     var content = config.content;
35467
35468     if(config.autoCreate){ // xtype is available if this is called from factory
35469         el = Roo.id();
35470     }
35471     this.el = Roo.get(el);
35472     if(!this.el && config && config.autoCreate){
35473         if(typeof config.autoCreate == "object"){
35474             if(!config.autoCreate.id){
35475                 config.autoCreate.id = config.id||el;
35476             }
35477             this.el = Roo.DomHelper.append(document.body,
35478                         config.autoCreate, true);
35479         }else{
35480             var elcfg =  {   tag: "div",
35481                             cls: "roo-layout-inactive-content",
35482                             id: config.id||el
35483                             };
35484             if (config.html) {
35485                 elcfg.html = config.html;
35486                 
35487             }
35488                         
35489             this.el = Roo.DomHelper.append(document.body, elcfg , true);
35490         }
35491     } 
35492     this.closable = false;
35493     this.loaded = false;
35494     this.active = false;
35495    
35496       
35497     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
35498         
35499         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
35500         
35501         this.wrapEl = this.el; //this.el.wrap();
35502         var ti = [];
35503         if (config.toolbar.items) {
35504             ti = config.toolbar.items ;
35505             delete config.toolbar.items ;
35506         }
35507         
35508         var nitems = [];
35509         this.toolbar.render(this.wrapEl, 'before');
35510         for(var i =0;i < ti.length;i++) {
35511           //  Roo.log(['add child', items[i]]);
35512             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
35513         }
35514         this.toolbar.items = nitems;
35515         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
35516         delete config.toolbar;
35517         
35518     }
35519     /*
35520     // xtype created footer. - not sure if will work as we normally have to render first..
35521     if (this.footer && !this.footer.el && this.footer.xtype) {
35522         if (!this.wrapEl) {
35523             this.wrapEl = this.el.wrap();
35524         }
35525     
35526         this.footer.container = this.wrapEl.createChild();
35527          
35528         this.footer = Roo.factory(this.footer, Roo);
35529         
35530     }
35531     */
35532     
35533      if(typeof config == "string"){
35534         this.title = config;
35535     }else{
35536         Roo.apply(this, config);
35537     }
35538     
35539     if(this.resizeEl){
35540         this.resizeEl = Roo.get(this.resizeEl, true);
35541     }else{
35542         this.resizeEl = this.el;
35543     }
35544     // handle view.xtype
35545     
35546  
35547     
35548     
35549     this.addEvents({
35550         /**
35551          * @event activate
35552          * Fires when this panel is activated. 
35553          * @param {Roo.ContentPanel} this
35554          */
35555         "activate" : true,
35556         /**
35557          * @event deactivate
35558          * Fires when this panel is activated. 
35559          * @param {Roo.ContentPanel} this
35560          */
35561         "deactivate" : true,
35562
35563         /**
35564          * @event resize
35565          * Fires when this panel is resized if fitToFrame is true.
35566          * @param {Roo.ContentPanel} this
35567          * @param {Number} width The width after any component adjustments
35568          * @param {Number} height The height after any component adjustments
35569          */
35570         "resize" : true,
35571         
35572          /**
35573          * @event render
35574          * Fires when this tab is created
35575          * @param {Roo.ContentPanel} this
35576          */
35577         "render" : true
35578         
35579         
35580         
35581     });
35582     
35583
35584     
35585     
35586     if(this.autoScroll){
35587         this.resizeEl.setStyle("overflow", "auto");
35588     } else {
35589         // fix randome scrolling
35590         //this.el.on('scroll', function() {
35591         //    Roo.log('fix random scolling');
35592         //    this.scrollTo('top',0); 
35593         //});
35594     }
35595     content = content || this.content;
35596     if(content){
35597         this.setContent(content);
35598     }
35599     if(config && config.url){
35600         this.setUrl(this.url, this.params, this.loadOnce);
35601     }
35602     
35603     
35604     
35605     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
35606     
35607     if (this.view && typeof(this.view.xtype) != 'undefined') {
35608         this.view.el = this.el.appendChild(document.createElement("div"));
35609         this.view = Roo.factory(this.view); 
35610         this.view.render  &&  this.view.render(false, '');  
35611     }
35612     
35613     
35614     this.fireEvent('render', this);
35615 };
35616
35617 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
35618     
35619     tabTip : '',
35620     
35621     setRegion : function(region){
35622         this.region = region;
35623         this.setActiveClass(region && !this.background);
35624     },
35625     
35626     
35627     setActiveClass: function(state)
35628     {
35629         if(state){
35630            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
35631            this.el.setStyle('position','relative');
35632         }else{
35633            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
35634            this.el.setStyle('position', 'absolute');
35635         } 
35636     },
35637     
35638     /**
35639      * Returns the toolbar for this Panel if one was configured. 
35640      * @return {Roo.Toolbar} 
35641      */
35642     getToolbar : function(){
35643         return this.toolbar;
35644     },
35645     
35646     setActiveState : function(active)
35647     {
35648         this.active = active;
35649         this.setActiveClass(active);
35650         if(!active){
35651             this.fireEvent("deactivate", this);
35652         }else{
35653             this.fireEvent("activate", this);
35654         }
35655     },
35656     /**
35657      * Updates this panel's element
35658      * @param {String} content The new content
35659      * @param {Boolean} loadScripts (optional) true to look for and process scripts
35660     */
35661     setContent : function(content, loadScripts){
35662         this.el.update(content, loadScripts);
35663     },
35664
35665     ignoreResize : function(w, h){
35666         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35667             return true;
35668         }else{
35669             this.lastSize = {width: w, height: h};
35670             return false;
35671         }
35672     },
35673     /**
35674      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35675      * @return {Roo.UpdateManager} The UpdateManager
35676      */
35677     getUpdateManager : function(){
35678         return this.el.getUpdateManager();
35679     },
35680      /**
35681      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35682      * @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:
35683 <pre><code>
35684 panel.load({
35685     url: "your-url.php",
35686     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35687     callback: yourFunction,
35688     scope: yourObject, //(optional scope)
35689     discardUrl: false,
35690     nocache: false,
35691     text: "Loading...",
35692     timeout: 30,
35693     scripts: false
35694 });
35695 </code></pre>
35696      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35697      * 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.
35698      * @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}
35699      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35700      * @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.
35701      * @return {Roo.ContentPanel} this
35702      */
35703     load : function(){
35704         var um = this.el.getUpdateManager();
35705         um.update.apply(um, arguments);
35706         return this;
35707     },
35708
35709
35710     /**
35711      * 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.
35712      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35713      * @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)
35714      * @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)
35715      * @return {Roo.UpdateManager} The UpdateManager
35716      */
35717     setUrl : function(url, params, loadOnce){
35718         if(this.refreshDelegate){
35719             this.removeListener("activate", this.refreshDelegate);
35720         }
35721         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35722         this.on("activate", this.refreshDelegate);
35723         return this.el.getUpdateManager();
35724     },
35725     
35726     _handleRefresh : function(url, params, loadOnce){
35727         if(!loadOnce || !this.loaded){
35728             var updater = this.el.getUpdateManager();
35729             updater.update(url, params, this._setLoaded.createDelegate(this));
35730         }
35731     },
35732     
35733     _setLoaded : function(){
35734         this.loaded = true;
35735     }, 
35736     
35737     /**
35738      * Returns this panel's id
35739      * @return {String} 
35740      */
35741     getId : function(){
35742         return this.el.id;
35743     },
35744     
35745     /** 
35746      * Returns this panel's element - used by regiosn to add.
35747      * @return {Roo.Element} 
35748      */
35749     getEl : function(){
35750         return this.wrapEl || this.el;
35751     },
35752     
35753    
35754     
35755     adjustForComponents : function(width, height)
35756     {
35757         //Roo.log('adjustForComponents ');
35758         if(this.resizeEl != this.el){
35759             width -= this.el.getFrameWidth('lr');
35760             height -= this.el.getFrameWidth('tb');
35761         }
35762         if(this.toolbar){
35763             var te = this.toolbar.getEl();
35764             height -= te.getHeight();
35765             te.setWidth(width);
35766         }
35767         if(this.footer){
35768             var te = this.footer.getEl();
35769             Roo.log("footer:" + te.getHeight());
35770             
35771             height -= te.getHeight();
35772             te.setWidth(width);
35773         }
35774         
35775         
35776         if(this.adjustments){
35777             width += this.adjustments[0];
35778             height += this.adjustments[1];
35779         }
35780         return {"width": width, "height": height};
35781     },
35782     
35783     setSize : function(width, height){
35784         if(this.fitToFrame && !this.ignoreResize(width, height)){
35785             if(this.fitContainer && this.resizeEl != this.el){
35786                 this.el.setSize(width, height);
35787             }
35788             var size = this.adjustForComponents(width, height);
35789             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
35790             this.fireEvent('resize', this, size.width, size.height);
35791         }
35792     },
35793     
35794     /**
35795      * Returns this panel's title
35796      * @return {String} 
35797      */
35798     getTitle : function(){
35799         return this.title;
35800     },
35801     
35802     /**
35803      * Set this panel's title
35804      * @param {String} title
35805      */
35806     setTitle : function(title){
35807         this.title = title;
35808         if(this.region){
35809             this.region.updatePanelTitle(this, title);
35810         }
35811     },
35812     
35813     /**
35814      * Returns true is this panel was configured to be closable
35815      * @return {Boolean} 
35816      */
35817     isClosable : function(){
35818         return this.closable;
35819     },
35820     
35821     beforeSlide : function(){
35822         this.el.clip();
35823         this.resizeEl.clip();
35824     },
35825     
35826     afterSlide : function(){
35827         this.el.unclip();
35828         this.resizeEl.unclip();
35829     },
35830     
35831     /**
35832      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
35833      *   Will fail silently if the {@link #setUrl} method has not been called.
35834      *   This does not activate the panel, just updates its content.
35835      */
35836     refresh : function(){
35837         if(this.refreshDelegate){
35838            this.loaded = false;
35839            this.refreshDelegate();
35840         }
35841     },
35842     
35843     /**
35844      * Destroys this panel
35845      */
35846     destroy : function(){
35847         this.el.removeAllListeners();
35848         var tempEl = document.createElement("span");
35849         tempEl.appendChild(this.el.dom);
35850         tempEl.innerHTML = "";
35851         this.el.remove();
35852         this.el = null;
35853     },
35854     
35855     /**
35856      * form - if the content panel contains a form - this is a reference to it.
35857      * @type {Roo.form.Form}
35858      */
35859     form : false,
35860     /**
35861      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35862      *    This contains a reference to it.
35863      * @type {Roo.View}
35864      */
35865     view : false,
35866     
35867       /**
35868      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35869      * <pre><code>
35870
35871 layout.addxtype({
35872        xtype : 'Form',
35873        items: [ .... ]
35874    }
35875 );
35876
35877 </code></pre>
35878      * @param {Object} cfg Xtype definition of item to add.
35879      */
35880     
35881     
35882     getChildContainer: function () {
35883         return this.getEl();
35884     }
35885     
35886     
35887     /*
35888         var  ret = new Roo.factory(cfg);
35889         return ret;
35890         
35891         
35892         // add form..
35893         if (cfg.xtype.match(/^Form$/)) {
35894             
35895             var el;
35896             //if (this.footer) {
35897             //    el = this.footer.container.insertSibling(false, 'before');
35898             //} else {
35899                 el = this.el.createChild();
35900             //}
35901
35902             this.form = new  Roo.form.Form(cfg);
35903             
35904             
35905             if ( this.form.allItems.length) {
35906                 this.form.render(el.dom);
35907             }
35908             return this.form;
35909         }
35910         // should only have one of theses..
35911         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35912             // views.. should not be just added - used named prop 'view''
35913             
35914             cfg.el = this.el.appendChild(document.createElement("div"));
35915             // factory?
35916             
35917             var ret = new Roo.factory(cfg);
35918              
35919              ret.render && ret.render(false, ''); // render blank..
35920             this.view = ret;
35921             return ret;
35922         }
35923         return false;
35924     }
35925     \*/
35926 });
35927  
35928 /**
35929  * @class Roo.bootstrap.panel.Grid
35930  * @extends Roo.bootstrap.panel.Content
35931  * @constructor
35932  * Create a new GridPanel.
35933  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
35934  * @param {Object} config A the config object
35935   
35936  */
35937
35938
35939
35940 Roo.bootstrap.panel.Grid = function(config)
35941 {
35942     
35943       
35944     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35945         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
35946
35947     config.el = this.wrapper;
35948     //this.el = this.wrapper;
35949     
35950       if (config.container) {
35951         // ctor'ed from a Border/panel.grid
35952         
35953         
35954         this.wrapper.setStyle("overflow", "hidden");
35955         this.wrapper.addClass('roo-grid-container');
35956
35957     }
35958     
35959     
35960     if(config.toolbar){
35961         var tool_el = this.wrapper.createChild();    
35962         this.toolbar = Roo.factory(config.toolbar);
35963         var ti = [];
35964         if (config.toolbar.items) {
35965             ti = config.toolbar.items ;
35966             delete config.toolbar.items ;
35967         }
35968         
35969         var nitems = [];
35970         this.toolbar.render(tool_el);
35971         for(var i =0;i < ti.length;i++) {
35972           //  Roo.log(['add child', items[i]]);
35973             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
35974         }
35975         this.toolbar.items = nitems;
35976         
35977         delete config.toolbar;
35978     }
35979     
35980     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
35981     config.grid.scrollBody = true;;
35982     config.grid.monitorWindowResize = false; // turn off autosizing
35983     config.grid.autoHeight = false;
35984     config.grid.autoWidth = false;
35985     
35986     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
35987     
35988     if (config.background) {
35989         // render grid on panel activation (if panel background)
35990         this.on('activate', function(gp) {
35991             if (!gp.grid.rendered) {
35992                 gp.grid.render(this.wrapper);
35993                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
35994             }
35995         });
35996             
35997     } else {
35998         this.grid.render(this.wrapper);
35999         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
36000
36001     }
36002     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36003     // ??? needed ??? config.el = this.wrapper;
36004     
36005     
36006     
36007   
36008     // xtype created footer. - not sure if will work as we normally have to render first..
36009     if (this.footer && !this.footer.el && this.footer.xtype) {
36010         
36011         var ctr = this.grid.getView().getFooterPanel(true);
36012         this.footer.dataSource = this.grid.dataSource;
36013         this.footer = Roo.factory(this.footer, Roo);
36014         this.footer.render(ctr);
36015         
36016     }
36017     
36018     
36019     
36020     
36021      
36022 };
36023
36024 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
36025     getId : function(){
36026         return this.grid.id;
36027     },
36028     
36029     /**
36030      * Returns the grid for this panel
36031      * @return {Roo.bootstrap.Table} 
36032      */
36033     getGrid : function(){
36034         return this.grid;    
36035     },
36036     
36037     setSize : function(width, height){
36038         if(!this.ignoreResize(width, height)){
36039             var grid = this.grid;
36040             var size = this.adjustForComponents(width, height);
36041             var gridel = grid.getGridEl();
36042             gridel.setSize(size.width, size.height);
36043             /*
36044             var thd = grid.getGridEl().select('thead',true).first();
36045             var tbd = grid.getGridEl().select('tbody', true).first();
36046             if (tbd) {
36047                 tbd.setSize(width, height - thd.getHeight());
36048             }
36049             */
36050             grid.autoSize();
36051         }
36052     },
36053      
36054     
36055     
36056     beforeSlide : function(){
36057         this.grid.getView().scroller.clip();
36058     },
36059     
36060     afterSlide : function(){
36061         this.grid.getView().scroller.unclip();
36062     },
36063     
36064     destroy : function(){
36065         this.grid.destroy();
36066         delete this.grid;
36067         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
36068     }
36069 });
36070
36071 /**
36072  * @class Roo.bootstrap.panel.Nest
36073  * @extends Roo.bootstrap.panel.Content
36074  * @constructor
36075  * Create a new Panel, that can contain a layout.Border.
36076  * 
36077  * 
36078  * @param {Roo.BorderLayout} layout The layout for this panel
36079  * @param {String/Object} config A string to set only the title or a config object
36080  */
36081 Roo.bootstrap.panel.Nest = function(config)
36082 {
36083     // construct with only one argument..
36084     /* FIXME - implement nicer consturctors
36085     if (layout.layout) {
36086         config = layout;
36087         layout = config.layout;
36088         delete config.layout;
36089     }
36090     if (layout.xtype && !layout.getEl) {
36091         // then layout needs constructing..
36092         layout = Roo.factory(layout, Roo);
36093     }
36094     */
36095     
36096     config.el =  config.layout.getEl();
36097     
36098     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
36099     
36100     config.layout.monitorWindowResize = false; // turn off autosizing
36101     this.layout = config.layout;
36102     this.layout.getEl().addClass("roo-layout-nested-layout");
36103     
36104     
36105     
36106     
36107 };
36108
36109 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
36110
36111     setSize : function(width, height){
36112         if(!this.ignoreResize(width, height)){
36113             var size = this.adjustForComponents(width, height);
36114             var el = this.layout.getEl();
36115             if (size.height < 1) {
36116                 el.setWidth(size.width);   
36117             } else {
36118                 el.setSize(size.width, size.height);
36119             }
36120             var touch = el.dom.offsetWidth;
36121             this.layout.layout();
36122             // ie requires a double layout on the first pass
36123             if(Roo.isIE && !this.initialized){
36124                 this.initialized = true;
36125                 this.layout.layout();
36126             }
36127         }
36128     },
36129     
36130     // activate all subpanels if not currently active..
36131     
36132     setActiveState : function(active){
36133         this.active = active;
36134         this.setActiveClass(active);
36135         
36136         if(!active){
36137             this.fireEvent("deactivate", this);
36138             return;
36139         }
36140         
36141         this.fireEvent("activate", this);
36142         // not sure if this should happen before or after..
36143         if (!this.layout) {
36144             return; // should not happen..
36145         }
36146         var reg = false;
36147         for (var r in this.layout.regions) {
36148             reg = this.layout.getRegion(r);
36149             if (reg.getActivePanel()) {
36150                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
36151                 reg.setActivePanel(reg.getActivePanel());
36152                 continue;
36153             }
36154             if (!reg.panels.length) {
36155                 continue;
36156             }
36157             reg.showPanel(reg.getPanel(0));
36158         }
36159         
36160         
36161         
36162         
36163     },
36164     
36165     /**
36166      * Returns the nested BorderLayout for this panel
36167      * @return {Roo.BorderLayout} 
36168      */
36169     getLayout : function(){
36170         return this.layout;
36171     },
36172     
36173      /**
36174      * Adds a xtype elements to the layout of the nested panel
36175      * <pre><code>
36176
36177 panel.addxtype({
36178        xtype : 'ContentPanel',
36179        region: 'west',
36180        items: [ .... ]
36181    }
36182 );
36183
36184 panel.addxtype({
36185         xtype : 'NestedLayoutPanel',
36186         region: 'west',
36187         layout: {
36188            center: { },
36189            west: { }   
36190         },
36191         items : [ ... list of content panels or nested layout panels.. ]
36192    }
36193 );
36194 </code></pre>
36195      * @param {Object} cfg Xtype definition of item to add.
36196      */
36197     addxtype : function(cfg) {
36198         return this.layout.addxtype(cfg);
36199     
36200     }
36201 });        /*
36202  * Based on:
36203  * Ext JS Library 1.1.1
36204  * Copyright(c) 2006-2007, Ext JS, LLC.
36205  *
36206  * Originally Released Under LGPL - original licence link has changed is not relivant.
36207  *
36208  * Fork - LGPL
36209  * <script type="text/javascript">
36210  */
36211 /**
36212  * @class Roo.TabPanel
36213  * @extends Roo.util.Observable
36214  * A lightweight tab container.
36215  * <br><br>
36216  * Usage:
36217  * <pre><code>
36218 // basic tabs 1, built from existing content
36219 var tabs = new Roo.TabPanel("tabs1");
36220 tabs.addTab("script", "View Script");
36221 tabs.addTab("markup", "View Markup");
36222 tabs.activate("script");
36223
36224 // more advanced tabs, built from javascript
36225 var jtabs = new Roo.TabPanel("jtabs");
36226 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
36227
36228 // set up the UpdateManager
36229 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
36230 var updater = tab2.getUpdateManager();
36231 updater.setDefaultUrl("ajax1.htm");
36232 tab2.on('activate', updater.refresh, updater, true);
36233
36234 // Use setUrl for Ajax loading
36235 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
36236 tab3.setUrl("ajax2.htm", null, true);
36237
36238 // Disabled tab
36239 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
36240 tab4.disable();
36241
36242 jtabs.activate("jtabs-1");
36243  * </code></pre>
36244  * @constructor
36245  * Create a new TabPanel.
36246  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
36247  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
36248  */
36249 Roo.bootstrap.panel.Tabs = function(config){
36250     /**
36251     * The container element for this TabPanel.
36252     * @type Roo.Element
36253     */
36254     this.el = Roo.get(config.el);
36255     delete config.el;
36256     if(config){
36257         if(typeof config == "boolean"){
36258             this.tabPosition = config ? "bottom" : "top";
36259         }else{
36260             Roo.apply(this, config);
36261         }
36262     }
36263     
36264     if(this.tabPosition == "bottom"){
36265         this.bodyEl = Roo.get(this.createBody(this.el.dom));
36266         this.el.addClass("roo-tabs-bottom");
36267     }
36268     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
36269     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
36270     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
36271     if(Roo.isIE){
36272         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
36273     }
36274     if(this.tabPosition != "bottom"){
36275         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
36276          * @type Roo.Element
36277          */
36278         this.bodyEl = Roo.get(this.createBody(this.el.dom));
36279         this.el.addClass("roo-tabs-top");
36280     }
36281     this.items = [];
36282
36283     this.bodyEl.setStyle("position", "relative");
36284
36285     this.active = null;
36286     this.activateDelegate = this.activate.createDelegate(this);
36287
36288     this.addEvents({
36289         /**
36290          * @event tabchange
36291          * Fires when the active tab changes
36292          * @param {Roo.TabPanel} this
36293          * @param {Roo.TabPanelItem} activePanel The new active tab
36294          */
36295         "tabchange": true,
36296         /**
36297          * @event beforetabchange
36298          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
36299          * @param {Roo.TabPanel} this
36300          * @param {Object} e Set cancel to true on this object to cancel the tab change
36301          * @param {Roo.TabPanelItem} tab The tab being changed to
36302          */
36303         "beforetabchange" : true
36304     });
36305
36306     Roo.EventManager.onWindowResize(this.onResize, this);
36307     this.cpad = this.el.getPadding("lr");
36308     this.hiddenCount = 0;
36309
36310
36311     // toolbar on the tabbar support...
36312     if (this.toolbar) {
36313         alert("no toolbar support yet");
36314         this.toolbar  = false;
36315         /*
36316         var tcfg = this.toolbar;
36317         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
36318         this.toolbar = new Roo.Toolbar(tcfg);
36319         if (Roo.isSafari) {
36320             var tbl = tcfg.container.child('table', true);
36321             tbl.setAttribute('width', '100%');
36322         }
36323         */
36324         
36325     }
36326    
36327
36328
36329     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
36330 };
36331
36332 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
36333     /*
36334      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
36335      */
36336     tabPosition : "top",
36337     /*
36338      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
36339      */
36340     currentTabWidth : 0,
36341     /*
36342      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
36343      */
36344     minTabWidth : 40,
36345     /*
36346      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
36347      */
36348     maxTabWidth : 250,
36349     /*
36350      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
36351      */
36352     preferredTabWidth : 175,
36353     /*
36354      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
36355      */
36356     resizeTabs : false,
36357     /*
36358      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
36359      */
36360     monitorResize : true,
36361     /*
36362      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
36363      */
36364     toolbar : false,
36365
36366     /**
36367      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
36368      * @param {String} id The id of the div to use <b>or create</b>
36369      * @param {String} text The text for the tab
36370      * @param {String} content (optional) Content to put in the TabPanelItem body
36371      * @param {Boolean} closable (optional) True to create a close icon on the tab
36372      * @return {Roo.TabPanelItem} The created TabPanelItem
36373      */
36374     addTab : function(id, text, content, closable, tpl)
36375     {
36376         var item = new Roo.bootstrap.panel.TabItem({
36377             panel: this,
36378             id : id,
36379             text : text,
36380             closable : closable,
36381             tpl : tpl
36382         });
36383         this.addTabItem(item);
36384         if(content){
36385             item.setContent(content);
36386         }
36387         return item;
36388     },
36389
36390     /**
36391      * Returns the {@link Roo.TabPanelItem} with the specified id/index
36392      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
36393      * @return {Roo.TabPanelItem}
36394      */
36395     getTab : function(id){
36396         return this.items[id];
36397     },
36398
36399     /**
36400      * Hides the {@link Roo.TabPanelItem} with the specified id/index
36401      * @param {String/Number} id The id or index of the TabPanelItem to hide.
36402      */
36403     hideTab : function(id){
36404         var t = this.items[id];
36405         if(!t.isHidden()){
36406            t.setHidden(true);
36407            this.hiddenCount++;
36408            this.autoSizeTabs();
36409         }
36410     },
36411
36412     /**
36413      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
36414      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
36415      */
36416     unhideTab : function(id){
36417         var t = this.items[id];
36418         if(t.isHidden()){
36419            t.setHidden(false);
36420            this.hiddenCount--;
36421            this.autoSizeTabs();
36422         }
36423     },
36424
36425     /**
36426      * Adds an existing {@link Roo.TabPanelItem}.
36427      * @param {Roo.TabPanelItem} item The TabPanelItem to add
36428      */
36429     addTabItem : function(item){
36430         this.items[item.id] = item;
36431         this.items.push(item);
36432       //  if(this.resizeTabs){
36433     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
36434   //         this.autoSizeTabs();
36435 //        }else{
36436 //            item.autoSize();
36437        // }
36438     },
36439
36440     /**
36441      * Removes a {@link Roo.TabPanelItem}.
36442      * @param {String/Number} id The id or index of the TabPanelItem to remove.
36443      */
36444     removeTab : function(id){
36445         var items = this.items;
36446         var tab = items[id];
36447         if(!tab) { return; }
36448         var index = items.indexOf(tab);
36449         if(this.active == tab && items.length > 1){
36450             var newTab = this.getNextAvailable(index);
36451             if(newTab) {
36452                 newTab.activate();
36453             }
36454         }
36455         this.stripEl.dom.removeChild(tab.pnode.dom);
36456         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
36457             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
36458         }
36459         items.splice(index, 1);
36460         delete this.items[tab.id];
36461         tab.fireEvent("close", tab);
36462         tab.purgeListeners();
36463         this.autoSizeTabs();
36464     },
36465
36466     getNextAvailable : function(start){
36467         var items = this.items;
36468         var index = start;
36469         // look for a next tab that will slide over to
36470         // replace the one being removed
36471         while(index < items.length){
36472             var item = items[++index];
36473             if(item && !item.isHidden()){
36474                 return item;
36475             }
36476         }
36477         // if one isn't found select the previous tab (on the left)
36478         index = start;
36479         while(index >= 0){
36480             var item = items[--index];
36481             if(item && !item.isHidden()){
36482                 return item;
36483             }
36484         }
36485         return null;
36486     },
36487
36488     /**
36489      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
36490      * @param {String/Number} id The id or index of the TabPanelItem to disable.
36491      */
36492     disableTab : function(id){
36493         var tab = this.items[id];
36494         if(tab && this.active != tab){
36495             tab.disable();
36496         }
36497     },
36498
36499     /**
36500      * Enables a {@link Roo.TabPanelItem} that is disabled.
36501      * @param {String/Number} id The id or index of the TabPanelItem to enable.
36502      */
36503     enableTab : function(id){
36504         var tab = this.items[id];
36505         tab.enable();
36506     },
36507
36508     /**
36509      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
36510      * @param {String/Number} id The id or index of the TabPanelItem to activate.
36511      * @return {Roo.TabPanelItem} The TabPanelItem.
36512      */
36513     activate : function(id){
36514         var tab = this.items[id];
36515         if(!tab){
36516             return null;
36517         }
36518         if(tab == this.active || tab.disabled){
36519             return tab;
36520         }
36521         var e = {};
36522         this.fireEvent("beforetabchange", this, e, tab);
36523         if(e.cancel !== true && !tab.disabled){
36524             if(this.active){
36525                 this.active.hide();
36526             }
36527             this.active = this.items[id];
36528             this.active.show();
36529             this.fireEvent("tabchange", this, this.active);
36530         }
36531         return tab;
36532     },
36533
36534     /**
36535      * Gets the active {@link Roo.TabPanelItem}.
36536      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
36537      */
36538     getActiveTab : function(){
36539         return this.active;
36540     },
36541
36542     /**
36543      * Updates the tab body element to fit the height of the container element
36544      * for overflow scrolling
36545      * @param {Number} targetHeight (optional) Override the starting height from the elements height
36546      */
36547     syncHeight : function(targetHeight){
36548         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36549         var bm = this.bodyEl.getMargins();
36550         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
36551         this.bodyEl.setHeight(newHeight);
36552         return newHeight;
36553     },
36554
36555     onResize : function(){
36556         if(this.monitorResize){
36557             this.autoSizeTabs();
36558         }
36559     },
36560
36561     /**
36562      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
36563      */
36564     beginUpdate : function(){
36565         this.updating = true;
36566     },
36567
36568     /**
36569      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
36570      */
36571     endUpdate : function(){
36572         this.updating = false;
36573         this.autoSizeTabs();
36574     },
36575
36576     /**
36577      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
36578      */
36579     autoSizeTabs : function(){
36580         var count = this.items.length;
36581         var vcount = count - this.hiddenCount;
36582         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
36583             return;
36584         }
36585         var w = Math.max(this.el.getWidth() - this.cpad, 10);
36586         var availWidth = Math.floor(w / vcount);
36587         var b = this.stripBody;
36588         if(b.getWidth() > w){
36589             var tabs = this.items;
36590             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
36591             if(availWidth < this.minTabWidth){
36592                 /*if(!this.sleft){    // incomplete scrolling code
36593                     this.createScrollButtons();
36594                 }
36595                 this.showScroll();
36596                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
36597             }
36598         }else{
36599             if(this.currentTabWidth < this.preferredTabWidth){
36600                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
36601             }
36602         }
36603     },
36604
36605     /**
36606      * Returns the number of tabs in this TabPanel.
36607      * @return {Number}
36608      */
36609      getCount : function(){
36610          return this.items.length;
36611      },
36612
36613     /**
36614      * Resizes all the tabs to the passed width
36615      * @param {Number} The new width
36616      */
36617     setTabWidth : function(width){
36618         this.currentTabWidth = width;
36619         for(var i = 0, len = this.items.length; i < len; i++) {
36620                 if(!this.items[i].isHidden()) {
36621                 this.items[i].setWidth(width);
36622             }
36623         }
36624     },
36625
36626     /**
36627      * Destroys this TabPanel
36628      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
36629      */
36630     destroy : function(removeEl){
36631         Roo.EventManager.removeResizeListener(this.onResize, this);
36632         for(var i = 0, len = this.items.length; i < len; i++){
36633             this.items[i].purgeListeners();
36634         }
36635         if(removeEl === true){
36636             this.el.update("");
36637             this.el.remove();
36638         }
36639     },
36640     
36641     createStrip : function(container)
36642     {
36643         var strip = document.createElement("nav");
36644         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
36645         container.appendChild(strip);
36646         return strip;
36647     },
36648     
36649     createStripList : function(strip)
36650     {
36651         // div wrapper for retard IE
36652         // returns the "tr" element.
36653         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
36654         //'<div class="x-tabs-strip-wrap">'+
36655           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
36656           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
36657         return strip.firstChild; //.firstChild.firstChild.firstChild;
36658     },
36659     createBody : function(container)
36660     {
36661         var body = document.createElement("div");
36662         Roo.id(body, "tab-body");
36663         //Roo.fly(body).addClass("x-tabs-body");
36664         Roo.fly(body).addClass("tab-content");
36665         container.appendChild(body);
36666         return body;
36667     },
36668     createItemBody :function(bodyEl, id){
36669         var body = Roo.getDom(id);
36670         if(!body){
36671             body = document.createElement("div");
36672             body.id = id;
36673         }
36674         //Roo.fly(body).addClass("x-tabs-item-body");
36675         Roo.fly(body).addClass("tab-pane");
36676          bodyEl.insertBefore(body, bodyEl.firstChild);
36677         return body;
36678     },
36679     /** @private */
36680     createStripElements :  function(stripEl, text, closable, tpl)
36681     {
36682         var td = document.createElement("li"); // was td..
36683         
36684         
36685         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
36686         
36687         
36688         stripEl.appendChild(td);
36689         /*if(closable){
36690             td.className = "x-tabs-closable";
36691             if(!this.closeTpl){
36692                 this.closeTpl = new Roo.Template(
36693                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36694                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
36695                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
36696                 );
36697             }
36698             var el = this.closeTpl.overwrite(td, {"text": text});
36699             var close = el.getElementsByTagName("div")[0];
36700             var inner = el.getElementsByTagName("em")[0];
36701             return {"el": el, "close": close, "inner": inner};
36702         } else {
36703         */
36704         // not sure what this is..
36705 //            if(!this.tabTpl){
36706                 //this.tabTpl = new Roo.Template(
36707                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36708                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
36709                 //);
36710 //                this.tabTpl = new Roo.Template(
36711 //                   '<a href="#">' +
36712 //                   '<span unselectable="on"' +
36713 //                            (this.disableTooltips ? '' : ' title="{text}"') +
36714 //                            ' >{text}</span></a>'
36715 //                );
36716 //                
36717 //            }
36718
36719
36720             var template = tpl || this.tabTpl || false;
36721             
36722             if(!template){
36723                 
36724                 template = new Roo.Template(
36725                    '<a href="#">' +
36726                    '<span unselectable="on"' +
36727                             (this.disableTooltips ? '' : ' title="{text}"') +
36728                             ' >{text}</span></a>'
36729                 );
36730             }
36731             
36732             switch (typeof(template)) {
36733                 case 'object' :
36734                     break;
36735                 case 'string' :
36736                     template = new Roo.Template(template);
36737                     break;
36738                 default :
36739                     break;
36740             }
36741             
36742             var el = template.overwrite(td, {"text": text});
36743             
36744             var inner = el.getElementsByTagName("span")[0];
36745             
36746             return {"el": el, "inner": inner};
36747             
36748     }
36749         
36750     
36751 });
36752
36753 /**
36754  * @class Roo.TabPanelItem
36755  * @extends Roo.util.Observable
36756  * Represents an individual item (tab plus body) in a TabPanel.
36757  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
36758  * @param {String} id The id of this TabPanelItem
36759  * @param {String} text The text for the tab of this TabPanelItem
36760  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
36761  */
36762 Roo.bootstrap.panel.TabItem = function(config){
36763     /**
36764      * The {@link Roo.TabPanel} this TabPanelItem belongs to
36765      * @type Roo.TabPanel
36766      */
36767     this.tabPanel = config.panel;
36768     /**
36769      * The id for this TabPanelItem
36770      * @type String
36771      */
36772     this.id = config.id;
36773     /** @private */
36774     this.disabled = false;
36775     /** @private */
36776     this.text = config.text;
36777     /** @private */
36778     this.loaded = false;
36779     this.closable = config.closable;
36780
36781     /**
36782      * The body element for this TabPanelItem.
36783      * @type Roo.Element
36784      */
36785     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
36786     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
36787     this.bodyEl.setStyle("display", "block");
36788     this.bodyEl.setStyle("zoom", "1");
36789     //this.hideAction();
36790
36791     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
36792     /** @private */
36793     this.el = Roo.get(els.el);
36794     this.inner = Roo.get(els.inner, true);
36795     this.textEl = Roo.get(this.el.dom.firstChild, true);
36796     this.pnode = Roo.get(els.el.parentNode, true);
36797     this.el.on("mousedown", this.onTabMouseDown, this);
36798     this.el.on("click", this.onTabClick, this);
36799     /** @private */
36800     if(config.closable){
36801         var c = Roo.get(els.close, true);
36802         c.dom.title = this.closeText;
36803         c.addClassOnOver("close-over");
36804         c.on("click", this.closeClick, this);
36805      }
36806
36807     this.addEvents({
36808          /**
36809          * @event activate
36810          * Fires when this tab becomes the active tab.
36811          * @param {Roo.TabPanel} tabPanel The parent TabPanel
36812          * @param {Roo.TabPanelItem} this
36813          */
36814         "activate": true,
36815         /**
36816          * @event beforeclose
36817          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
36818          * @param {Roo.TabPanelItem} this
36819          * @param {Object} e Set cancel to true on this object to cancel the close.
36820          */
36821         "beforeclose": true,
36822         /**
36823          * @event close
36824          * Fires when this tab is closed.
36825          * @param {Roo.TabPanelItem} this
36826          */
36827          "close": true,
36828         /**
36829          * @event deactivate
36830          * Fires when this tab is no longer the active tab.
36831          * @param {Roo.TabPanel} tabPanel The parent TabPanel
36832          * @param {Roo.TabPanelItem} this
36833          */
36834          "deactivate" : true
36835     });
36836     this.hidden = false;
36837
36838     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
36839 };
36840
36841 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
36842            {
36843     purgeListeners : function(){
36844        Roo.util.Observable.prototype.purgeListeners.call(this);
36845        this.el.removeAllListeners();
36846     },
36847     /**
36848      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
36849      */
36850     show : function(){
36851         this.pnode.addClass("active");
36852         this.showAction();
36853         if(Roo.isOpera){
36854             this.tabPanel.stripWrap.repaint();
36855         }
36856         this.fireEvent("activate", this.tabPanel, this);
36857     },
36858
36859     /**
36860      * Returns true if this tab is the active tab.
36861      * @return {Boolean}
36862      */
36863     isActive : function(){
36864         return this.tabPanel.getActiveTab() == this;
36865     },
36866
36867     /**
36868      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
36869      */
36870     hide : function(){
36871         this.pnode.removeClass("active");
36872         this.hideAction();
36873         this.fireEvent("deactivate", this.tabPanel, this);
36874     },
36875
36876     hideAction : function(){
36877         this.bodyEl.hide();
36878         this.bodyEl.setStyle("position", "absolute");
36879         this.bodyEl.setLeft("-20000px");
36880         this.bodyEl.setTop("-20000px");
36881     },
36882
36883     showAction : function(){
36884         this.bodyEl.setStyle("position", "relative");
36885         this.bodyEl.setTop("");
36886         this.bodyEl.setLeft("");
36887         this.bodyEl.show();
36888     },
36889
36890     /**
36891      * Set the tooltip for the tab.
36892      * @param {String} tooltip The tab's tooltip
36893      */
36894     setTooltip : function(text){
36895         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
36896             this.textEl.dom.qtip = text;
36897             this.textEl.dom.removeAttribute('title');
36898         }else{
36899             this.textEl.dom.title = text;
36900         }
36901     },
36902
36903     onTabClick : function(e){
36904         e.preventDefault();
36905         this.tabPanel.activate(this.id);
36906     },
36907
36908     onTabMouseDown : function(e){
36909         e.preventDefault();
36910         this.tabPanel.activate(this.id);
36911     },
36912 /*
36913     getWidth : function(){
36914         return this.inner.getWidth();
36915     },
36916
36917     setWidth : function(width){
36918         var iwidth = width - this.pnode.getPadding("lr");
36919         this.inner.setWidth(iwidth);
36920         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
36921         this.pnode.setWidth(width);
36922     },
36923 */
36924     /**
36925      * Show or hide the tab
36926      * @param {Boolean} hidden True to hide or false to show.
36927      */
36928     setHidden : function(hidden){
36929         this.hidden = hidden;
36930         this.pnode.setStyle("display", hidden ? "none" : "");
36931     },
36932
36933     /**
36934      * Returns true if this tab is "hidden"
36935      * @return {Boolean}
36936      */
36937     isHidden : function(){
36938         return this.hidden;
36939     },
36940
36941     /**
36942      * Returns the text for this tab
36943      * @return {String}
36944      */
36945     getText : function(){
36946         return this.text;
36947     },
36948     /*
36949     autoSize : function(){
36950         //this.el.beginMeasure();
36951         this.textEl.setWidth(1);
36952         /*
36953          *  #2804 [new] Tabs in Roojs
36954          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
36955          */
36956         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
36957         //this.el.endMeasure();
36958     //},
36959
36960     /**
36961      * Sets the text for the tab (Note: this also sets the tooltip text)
36962      * @param {String} text The tab's text and tooltip
36963      */
36964     setText : function(text){
36965         this.text = text;
36966         this.textEl.update(text);
36967         this.setTooltip(text);
36968         //if(!this.tabPanel.resizeTabs){
36969         //    this.autoSize();
36970         //}
36971     },
36972     /**
36973      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
36974      */
36975     activate : function(){
36976         this.tabPanel.activate(this.id);
36977     },
36978
36979     /**
36980      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
36981      */
36982     disable : function(){
36983         if(this.tabPanel.active != this){
36984             this.disabled = true;
36985             this.pnode.addClass("disabled");
36986         }
36987     },
36988
36989     /**
36990      * Enables this TabPanelItem if it was previously disabled.
36991      */
36992     enable : function(){
36993         this.disabled = false;
36994         this.pnode.removeClass("disabled");
36995     },
36996
36997     /**
36998      * Sets the content for this TabPanelItem.
36999      * @param {String} content The content
37000      * @param {Boolean} loadScripts true to look for and load scripts
37001      */
37002     setContent : function(content, loadScripts){
37003         this.bodyEl.update(content, loadScripts);
37004     },
37005
37006     /**
37007      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37008      * @return {Roo.UpdateManager} The UpdateManager
37009      */
37010     getUpdateManager : function(){
37011         return this.bodyEl.getUpdateManager();
37012     },
37013
37014     /**
37015      * Set a URL to be used to load the content for this TabPanelItem.
37016      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37017      * @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)
37018      * @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)
37019      * @return {Roo.UpdateManager} The UpdateManager
37020      */
37021     setUrl : function(url, params, loadOnce){
37022         if(this.refreshDelegate){
37023             this.un('activate', this.refreshDelegate);
37024         }
37025         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37026         this.on("activate", this.refreshDelegate);
37027         return this.bodyEl.getUpdateManager();
37028     },
37029
37030     /** @private */
37031     _handleRefresh : function(url, params, loadOnce){
37032         if(!loadOnce || !this.loaded){
37033             var updater = this.bodyEl.getUpdateManager();
37034             updater.update(url, params, this._setLoaded.createDelegate(this));
37035         }
37036     },
37037
37038     /**
37039      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
37040      *   Will fail silently if the setUrl method has not been called.
37041      *   This does not activate the panel, just updates its content.
37042      */
37043     refresh : function(){
37044         if(this.refreshDelegate){
37045            this.loaded = false;
37046            this.refreshDelegate();
37047         }
37048     },
37049
37050     /** @private */
37051     _setLoaded : function(){
37052         this.loaded = true;
37053     },
37054
37055     /** @private */
37056     closeClick : function(e){
37057         var o = {};
37058         e.stopEvent();
37059         this.fireEvent("beforeclose", this, o);
37060         if(o.cancel !== true){
37061             this.tabPanel.removeTab(this.id);
37062         }
37063     },
37064     /**
37065      * The text displayed in the tooltip for the close icon.
37066      * @type String
37067      */
37068     closeText : "Close this tab"
37069 });