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.footerEl = this.el.select('.modal-footer',true).first();
2573         this.titleEl = this.el.select('.modal-title',true).first();
2574
2575
2576
2577         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2578         this.maskEl.enableDisplayMode("block");
2579         this.maskEl.hide();
2580         //this.el.addClass("x-dlg-modal");
2581
2582         if (this.buttons.length) {
2583             Roo.each(this.buttons, function(bb) {
2584                 var b = Roo.apply({}, bb);
2585                 b.xns = b.xns || Roo.bootstrap;
2586                 b.xtype = b.xtype || 'Button';
2587                 if (typeof(b.listeners) == 'undefined') {
2588                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2589                 }
2590
2591                 var btn = Roo.factory(b);
2592
2593                 btn.render(this.el.select('.modal-footer div').first());
2594
2595             },this);
2596         }
2597         // render the children.
2598         var nitems = [];
2599
2600         if(typeof(this.items) != 'undefined'){
2601             var items = this.items;
2602             delete this.items;
2603
2604             for(var i =0;i < items.length;i++) {
2605                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2606             }
2607         }
2608
2609         this.items = nitems;
2610
2611         // where are these used - they used to be body/close/footer
2612
2613
2614         this.initEvents();
2615         //this.el.addClass([this.fieldClass, this.cls]);
2616
2617     },
2618
2619     getAutoCreate : function(){
2620
2621
2622         var bdy = {
2623                 cls : 'modal-body',
2624                 html : this.html || ''
2625         };
2626
2627         var title = {
2628             tag: 'h4',
2629             cls : 'modal-title',
2630             html : this.title
2631         };
2632
2633         if(this.specificTitle){
2634             title = this.title;
2635
2636         };
2637
2638         var header = [];
2639         if (this.allow_close) {
2640             header.push({
2641                 tag: 'button',
2642                 cls : 'close',
2643                 html : '&times'
2644             });
2645         }
2646
2647         header.push(title);
2648
2649         var size = '';
2650
2651         if(this.size.length){
2652             size = 'modal-' + this.size;
2653         }
2654
2655         var modal = {
2656             cls: "modal",
2657             style : 'display: none',
2658             cn : [
2659                 {
2660                     cls: "modal-dialog " + size,
2661                     cn : [
2662                         {
2663                             cls : "modal-content",
2664                             cn : [
2665                                 {
2666                                     cls : 'modal-header',
2667                                     cn : header
2668                                 },
2669                                 bdy,
2670                                 {
2671                                     cls : 'modal-footer',
2672                                     cn : [
2673                                         {
2674                                             tag: 'div',
2675                                             cls: 'btn-' + this.buttonPosition
2676                                         }
2677                                     ]
2678
2679                                 }
2680
2681
2682                             ]
2683
2684                         }
2685                     ]
2686
2687                 }
2688             ]
2689         };
2690
2691         if(this.animate){
2692             modal.cls += ' fade';
2693         }
2694
2695         return modal;
2696
2697     },
2698     getChildContainer : function() {
2699
2700          return this.bodyEl;
2701
2702     },
2703     getButtonContainer : function() {
2704          return this.el.select('.modal-footer div',true).first();
2705
2706     },
2707     initEvents : function()
2708     {
2709         if (this.allow_close) {
2710             this.closeEl.on('click', this.hide, this);
2711         }
2712         Roo.EventManager.onWindowResize(this.resize, this, true);
2713
2714
2715     },
2716
2717     resize : function()
2718     {
2719         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2720         if (this.fitwindow) {
2721             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2722             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2723             this.setSize(w,h);
2724         }
2725     },
2726
2727     setSize : function(w,h)
2728     {
2729         if (!w && !h) {
2730             return;
2731         }
2732         this.resizeTo(w,h);
2733     },
2734
2735     show : function() {
2736
2737         if (!this.rendered) {
2738             this.render();
2739         }
2740
2741         this.el.setStyle('display', 'block');
2742
2743         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2744             var _this = this;
2745             (function(){
2746                 this.el.addClass('in');
2747             }).defer(50, this);
2748         }else{
2749             this.el.addClass('in');
2750
2751         }
2752
2753         // not sure how we can show data in here..
2754         //if (this.tmpl) {
2755         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2756         //}
2757
2758         Roo.get(document.body).addClass("x-body-masked");
2759         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2760         this.maskEl.show();
2761         this.el.setStyle('zIndex', '10001');
2762
2763         this.fireEvent('show', this);
2764
2765         this.resize();
2766
2767         (function () {
2768             this.items.forEach( function(e) {
2769                 e.layout ? e.layout() : false;
2770
2771             });
2772         }).defer(100,this);
2773
2774     },
2775     hide : function()
2776     {
2777         if(this.fireEvent("beforehide", this) !== false){
2778             this.maskEl.hide();
2779             Roo.get(document.body).removeClass("x-body-masked");
2780             this.el.removeClass('in');
2781             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2782
2783             if(this.animate){ // why
2784                 var _this = this;
2785                 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2786             }else{
2787                 this.el.setStyle('display', 'none');
2788             }
2789             this.fireEvent('hide', this);
2790         }
2791     },
2792
2793     addButton : function(str, cb)
2794     {
2795
2796
2797         var b = Roo.apply({}, { html : str } );
2798         b.xns = b.xns || Roo.bootstrap;
2799         b.xtype = b.xtype || 'Button';
2800         if (typeof(b.listeners) == 'undefined') {
2801             b.listeners = { click : cb.createDelegate(this)  };
2802         }
2803
2804         var btn = Roo.factory(b);
2805
2806         btn.render(this.el.select('.modal-footer div').first());
2807
2808         return btn;
2809
2810     },
2811
2812     setDefaultButton : function(btn)
2813     {
2814         //this.el.select('.modal-footer').()
2815     },
2816     diff : false,
2817
2818     resizeTo: function(w,h)
2819     {
2820         // skip.. ?? why??
2821
2822         this.dialogEl.setWidth(w);
2823         if (this.diff === false) {
2824             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2825         }
2826
2827         this.bodyEl.setHeight(h-this.diff);
2828
2829
2830     },
2831     setContentSize  : function(w, h)
2832     {
2833
2834     },
2835     onButtonClick: function(btn,e)
2836     {
2837         //Roo.log([a,b,c]);
2838         this.fireEvent('btnclick', btn.name, e);
2839     },
2840      /**
2841      * Set the title of the Dialog
2842      * @param {String} str new Title
2843      */
2844     setTitle: function(str) {
2845         this.titleEl.dom.innerHTML = str;
2846     },
2847     /**
2848      * Set the body of the Dialog
2849      * @param {String} str new Title
2850      */
2851     setBody: function(str) {
2852         this.bodyEl.dom.innerHTML = str;
2853     },
2854     /**
2855      * Set the body of the Dialog using the template
2856      * @param {Obj} data - apply this data to the template and replace the body contents.
2857      */
2858     applyBody: function(obj)
2859     {
2860         if (!this.tmpl) {
2861             Roo.log("Error - using apply Body without a template");
2862             //code
2863         }
2864         this.tmpl.overwrite(this.bodyEl, obj);
2865     }
2866
2867 });
2868
2869
2870 Roo.apply(Roo.bootstrap.Modal,  {
2871     /**
2872          * Button config that displays a single OK button
2873          * @type Object
2874          */
2875         OK :  [{
2876             name : 'ok',
2877             weight : 'primary',
2878             html : 'OK'
2879         }],
2880         /**
2881          * Button config that displays Yes and No buttons
2882          * @type Object
2883          */
2884         YESNO : [
2885             {
2886                 name  : 'no',
2887                 html : 'No'
2888             },
2889             {
2890                 name  :'yes',
2891                 weight : 'primary',
2892                 html : 'Yes'
2893             }
2894         ],
2895
2896         /**
2897          * Button config that displays OK and Cancel buttons
2898          * @type Object
2899          */
2900         OKCANCEL : [
2901             {
2902                name : 'cancel',
2903                 html : 'Cancel'
2904             },
2905             {
2906                 name : 'ok',
2907                 weight : 'primary',
2908                 html : 'OK'
2909             }
2910         ],
2911         /**
2912          * Button config that displays Yes, No and Cancel buttons
2913          * @type Object
2914          */
2915         YESNOCANCEL : [
2916             {
2917                 name : 'yes',
2918                 weight : 'primary',
2919                 html : 'Yes'
2920             },
2921             {
2922                 name : 'no',
2923                 html : 'No'
2924             },
2925             {
2926                 name : 'cancel',
2927                 html : 'Cancel'
2928             }
2929         ]
2930 });
2931 /*
2932  * - LGPL
2933  *
2934  * messagebox - can be used as a replace
2935  * 
2936  */
2937 /**
2938  * @class Roo.MessageBox
2939  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2940  * Example usage:
2941  *<pre><code>
2942 // Basic alert:
2943 Roo.Msg.alert('Status', 'Changes saved successfully.');
2944
2945 // Prompt for user data:
2946 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2947     if (btn == 'ok'){
2948         // process text value...
2949     }
2950 });
2951
2952 // Show a dialog using config options:
2953 Roo.Msg.show({
2954    title:'Save Changes?',
2955    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2956    buttons: Roo.Msg.YESNOCANCEL,
2957    fn: processResult,
2958    animEl: 'elId'
2959 });
2960 </code></pre>
2961  * @singleton
2962  */
2963 Roo.bootstrap.MessageBox = function(){
2964     var dlg, opt, mask, waitTimer;
2965     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2966     var buttons, activeTextEl, bwidth;
2967
2968     
2969     // private
2970     var handleButton = function(button){
2971         dlg.hide();
2972         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2973     };
2974
2975     // private
2976     var handleHide = function(){
2977         if(opt && opt.cls){
2978             dlg.el.removeClass(opt.cls);
2979         }
2980         //if(waitTimer){
2981         //    Roo.TaskMgr.stop(waitTimer);
2982         //    waitTimer = null;
2983         //}
2984     };
2985
2986     // private
2987     var updateButtons = function(b){
2988         var width = 0;
2989         if(!b){
2990             buttons["ok"].hide();
2991             buttons["cancel"].hide();
2992             buttons["yes"].hide();
2993             buttons["no"].hide();
2994             //dlg.footer.dom.style.display = 'none';
2995             return width;
2996         }
2997         dlg.footerEl.dom.style.display = '';
2998         for(var k in buttons){
2999             if(typeof buttons[k] != "function"){
3000                 if(b[k]){
3001                     buttons[k].show();
3002                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3003                     width += buttons[k].el.getWidth()+15;
3004                 }else{
3005                     buttons[k].hide();
3006                 }
3007             }
3008         }
3009         return width;
3010     };
3011
3012     // private
3013     var handleEsc = function(d, k, e){
3014         if(opt && opt.closable !== false){
3015             dlg.hide();
3016         }
3017         if(e){
3018             e.stopEvent();
3019         }
3020     };
3021
3022     return {
3023         /**
3024          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3025          * @return {Roo.BasicDialog} The BasicDialog element
3026          */
3027         getDialog : function(){
3028            if(!dlg){
3029                 dlg = new Roo.bootstrap.Modal( {
3030                     //draggable: true,
3031                     //resizable:false,
3032                     //constraintoviewport:false,
3033                     //fixedcenter:true,
3034                     //collapsible : false,
3035                     //shim:true,
3036                     //modal: true,
3037                   //  width:400,
3038                   //  height:100,
3039                     //buttonAlign:"center",
3040                     closeClick : function(){
3041                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3042                             handleButton("no");
3043                         }else{
3044                             handleButton("cancel");
3045                         }
3046                     }
3047                 });
3048                 dlg.render();
3049                 dlg.on("hide", handleHide);
3050                 mask = dlg.mask;
3051                 //dlg.addKeyListener(27, handleEsc);
3052                 buttons = {};
3053                 this.buttons = buttons;
3054                 var bt = this.buttonText;
3055                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3056                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3057                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3058                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3059                 //Roo.log(buttons);
3060                 bodyEl = dlg.bodyEl.createChild({
3061
3062                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3063                         '<textarea class="roo-mb-textarea"></textarea>' +
3064                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3065                 });
3066                 msgEl = bodyEl.dom.firstChild;
3067                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3068                 textboxEl.enableDisplayMode();
3069                 textboxEl.addKeyListener([10,13], function(){
3070                     if(dlg.isVisible() && opt && opt.buttons){
3071                         if(opt.buttons.ok){
3072                             handleButton("ok");
3073                         }else if(opt.buttons.yes){
3074                             handleButton("yes");
3075                         }
3076                     }
3077                 });
3078                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3079                 textareaEl.enableDisplayMode();
3080                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3081                 progressEl.enableDisplayMode();
3082                 var pf = progressEl.dom.firstChild;
3083                 if (pf) {
3084                     pp = Roo.get(pf.firstChild);
3085                     pp.setHeight(pf.offsetHeight);
3086                 }
3087                 
3088             }
3089             return dlg;
3090         },
3091
3092         /**
3093          * Updates the message box body text
3094          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3095          * the XHTML-compliant non-breaking space character '&amp;#160;')
3096          * @return {Roo.MessageBox} This message box
3097          */
3098         updateText : function(text){
3099             if(!dlg.isVisible() && !opt.width){
3100                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
3101             }
3102             msgEl.innerHTML = text || '&#160;';
3103       
3104             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3105             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3106             var w = Math.max(
3107                     Math.min(opt.width || cw , this.maxWidth), 
3108                     Math.max(opt.minWidth || this.minWidth, bwidth)
3109             );
3110             if(opt.prompt){
3111                 activeTextEl.setWidth(w);
3112             }
3113             if(dlg.isVisible()){
3114                 dlg.fixedcenter = false;
3115             }
3116             // to big, make it scroll. = But as usual stupid IE does not support
3117             // !important..
3118             
3119             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3120                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3121                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3122             } else {
3123                 bodyEl.dom.style.height = '';
3124                 bodyEl.dom.style.overflowY = '';
3125             }
3126             if (cw > w) {
3127                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3128             } else {
3129                 bodyEl.dom.style.overflowX = '';
3130             }
3131             
3132             dlg.setContentSize(w, bodyEl.getHeight());
3133             if(dlg.isVisible()){
3134                 dlg.fixedcenter = true;
3135             }
3136             return this;
3137         },
3138
3139         /**
3140          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3141          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3142          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3143          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3144          * @return {Roo.MessageBox} This message box
3145          */
3146         updateProgress : function(value, text){
3147             if(text){
3148                 this.updateText(text);
3149             }
3150             if (pp) { // weird bug on my firefox - for some reason this is not defined
3151                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3152             }
3153             return this;
3154         },        
3155
3156         /**
3157          * Returns true if the message box is currently displayed
3158          * @return {Boolean} True if the message box is visible, else false
3159          */
3160         isVisible : function(){
3161             return dlg && dlg.isVisible();  
3162         },
3163
3164         /**
3165          * Hides the message box if it is displayed
3166          */
3167         hide : function(){
3168             if(this.isVisible()){
3169                 dlg.hide();
3170             }  
3171         },
3172
3173         /**
3174          * Displays a new message box, or reinitializes an existing message box, based on the config options
3175          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3176          * The following config object properties are supported:
3177          * <pre>
3178 Property    Type             Description
3179 ----------  ---------------  ------------------------------------------------------------------------------------
3180 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3181                                    closes (defaults to undefined)
3182 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3183                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3184 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3185                                    progress and wait dialogs will ignore this property and always hide the
3186                                    close button as they can only be closed programmatically.
3187 cls               String           A custom CSS class to apply to the message box element
3188 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3189                                    displayed (defaults to 75)
3190 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3191                                    function will be btn (the name of the button that was clicked, if applicable,
3192                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3193                                    Progress and wait dialogs will ignore this option since they do not respond to
3194                                    user actions and can only be closed programmatically, so any required function
3195                                    should be called by the same code after it closes the dialog.
3196 icon              String           A CSS class that provides a background image to be used as an icon for
3197                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3198 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3199 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3200 modal             Boolean          False to allow user interaction with the page while the message box is
3201                                    displayed (defaults to true)
3202 msg               String           A string that will replace the existing message box body text (defaults
3203                                    to the XHTML-compliant non-breaking space character '&#160;')
3204 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3205 progress          Boolean          True to display a progress bar (defaults to false)
3206 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3207 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3208 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3209 title             String           The title text
3210 value             String           The string value to set into the active textbox element if displayed
3211 wait              Boolean          True to display a progress bar (defaults to false)
3212 width             Number           The width of the dialog in pixels
3213 </pre>
3214          *
3215          * Example usage:
3216          * <pre><code>
3217 Roo.Msg.show({
3218    title: 'Address',
3219    msg: 'Please enter your address:',
3220    width: 300,
3221    buttons: Roo.MessageBox.OKCANCEL,
3222    multiline: true,
3223    fn: saveAddress,
3224    animEl: 'addAddressBtn'
3225 });
3226 </code></pre>
3227          * @param {Object} config Configuration options
3228          * @return {Roo.MessageBox} This message box
3229          */
3230         show : function(options)
3231         {
3232             
3233             // this causes nightmares if you show one dialog after another
3234             // especially on callbacks..
3235              
3236             if(this.isVisible()){
3237                 
3238                 this.hide();
3239                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3240                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3241                 Roo.log("New Dialog Message:" +  options.msg )
3242                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3243                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3244                 
3245             }
3246             var d = this.getDialog();
3247             opt = options;
3248             d.setTitle(opt.title || "&#160;");
3249             d.closeEl.setDisplayed(opt.closable !== false);
3250             activeTextEl = textboxEl;
3251             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3252             if(opt.prompt){
3253                 if(opt.multiline){
3254                     textboxEl.hide();
3255                     textareaEl.show();
3256                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3257                         opt.multiline : this.defaultTextHeight);
3258                     activeTextEl = textareaEl;
3259                 }else{
3260                     textboxEl.show();
3261                     textareaEl.hide();
3262                 }
3263             }else{
3264                 textboxEl.hide();
3265                 textareaEl.hide();
3266             }
3267             progressEl.setDisplayed(opt.progress === true);
3268             this.updateProgress(0);
3269             activeTextEl.dom.value = opt.value || "";
3270             if(opt.prompt){
3271                 dlg.setDefaultButton(activeTextEl);
3272             }else{
3273                 var bs = opt.buttons;
3274                 var db = null;
3275                 if(bs && bs.ok){
3276                     db = buttons["ok"];
3277                 }else if(bs && bs.yes){
3278                     db = buttons["yes"];
3279                 }
3280                 dlg.setDefaultButton(db);
3281             }
3282             bwidth = updateButtons(opt.buttons);
3283             this.updateText(opt.msg);
3284             if(opt.cls){
3285                 d.el.addClass(opt.cls);
3286             }
3287             d.proxyDrag = opt.proxyDrag === true;
3288             d.modal = opt.modal !== false;
3289             d.mask = opt.modal !== false ? mask : false;
3290             if(!d.isVisible()){
3291                 // force it to the end of the z-index stack so it gets a cursor in FF
3292                 document.body.appendChild(dlg.el.dom);
3293                 d.animateTarget = null;
3294                 d.show(options.animEl);
3295             }
3296             return this;
3297         },
3298
3299         /**
3300          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3301          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3302          * and closing the message box when the process is complete.
3303          * @param {String} title The title bar text
3304          * @param {String} msg The message box body text
3305          * @return {Roo.MessageBox} This message box
3306          */
3307         progress : function(title, msg){
3308             this.show({
3309                 title : title,
3310                 msg : msg,
3311                 buttons: false,
3312                 progress:true,
3313                 closable:false,
3314                 minWidth: this.minProgressWidth,
3315                 modal : true
3316             });
3317             return this;
3318         },
3319
3320         /**
3321          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3322          * If a callback function is passed it will be called after the user clicks the button, and the
3323          * id of the button that was clicked will be passed as the only parameter to the callback
3324          * (could also be the top-right close button).
3325          * @param {String} title The title bar text
3326          * @param {String} msg The message box body text
3327          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3328          * @param {Object} scope (optional) The scope of the callback function
3329          * @return {Roo.MessageBox} This message box
3330          */
3331         alert : function(title, msg, fn, scope){
3332             this.show({
3333                 title : title,
3334                 msg : msg,
3335                 buttons: this.OK,
3336                 fn: fn,
3337                 scope : scope,
3338                 modal : true
3339             });
3340             return this;
3341         },
3342
3343         /**
3344          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3345          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3346          * You are responsible for closing the message box when the process is complete.
3347          * @param {String} msg The message box body text
3348          * @param {String} title (optional) The title bar text
3349          * @return {Roo.MessageBox} This message box
3350          */
3351         wait : function(msg, title){
3352             this.show({
3353                 title : title,
3354                 msg : msg,
3355                 buttons: false,
3356                 closable:false,
3357                 progress:true,
3358                 modal:true,
3359                 width:300,
3360                 wait:true
3361             });
3362             waitTimer = Roo.TaskMgr.start({
3363                 run: function(i){
3364                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3365                 },
3366                 interval: 1000
3367             });
3368             return this;
3369         },
3370
3371         /**
3372          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3373          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3374          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3375          * @param {String} title The title bar text
3376          * @param {String} msg The message box body text
3377          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3378          * @param {Object} scope (optional) The scope of the callback function
3379          * @return {Roo.MessageBox} This message box
3380          */
3381         confirm : function(title, msg, fn, scope){
3382             this.show({
3383                 title : title,
3384                 msg : msg,
3385                 buttons: this.YESNO,
3386                 fn: fn,
3387                 scope : scope,
3388                 modal : true
3389             });
3390             return this;
3391         },
3392
3393         /**
3394          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3395          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3396          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3397          * (could also be the top-right close button) and the text that was entered will be passed as the two
3398          * parameters to the callback.
3399          * @param {String} title The title bar text
3400          * @param {String} msg The message box body text
3401          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3402          * @param {Object} scope (optional) The scope of the callback function
3403          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3404          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3405          * @return {Roo.MessageBox} This message box
3406          */
3407         prompt : function(title, msg, fn, scope, multiline){
3408             this.show({
3409                 title : title,
3410                 msg : msg,
3411                 buttons: this.OKCANCEL,
3412                 fn: fn,
3413                 minWidth:250,
3414                 scope : scope,
3415                 prompt:true,
3416                 multiline: multiline,
3417                 modal : true
3418             });
3419             return this;
3420         },
3421
3422         /**
3423          * Button config that displays a single OK button
3424          * @type Object
3425          */
3426         OK : {ok:true},
3427         /**
3428          * Button config that displays Yes and No buttons
3429          * @type Object
3430          */
3431         YESNO : {yes:true, no:true},
3432         /**
3433          * Button config that displays OK and Cancel buttons
3434          * @type Object
3435          */
3436         OKCANCEL : {ok:true, cancel:true},
3437         /**
3438          * Button config that displays Yes, No and Cancel buttons
3439          * @type Object
3440          */
3441         YESNOCANCEL : {yes:true, no:true, cancel:true},
3442
3443         /**
3444          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3445          * @type Number
3446          */
3447         defaultTextHeight : 75,
3448         /**
3449          * The maximum width in pixels of the message box (defaults to 600)
3450          * @type Number
3451          */
3452         maxWidth : 600,
3453         /**
3454          * The minimum width in pixels of the message box (defaults to 100)
3455          * @type Number
3456          */
3457         minWidth : 100,
3458         /**
3459          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3460          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3461          * @type Number
3462          */
3463         minProgressWidth : 250,
3464         /**
3465          * An object containing the default button text strings that can be overriden for localized language support.
3466          * Supported properties are: ok, cancel, yes and no.
3467          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3468          * @type Object
3469          */
3470         buttonText : {
3471             ok : "OK",
3472             cancel : "Cancel",
3473             yes : "Yes",
3474             no : "No"
3475         }
3476     };
3477 }();
3478
3479 /**
3480  * Shorthand for {@link Roo.MessageBox}
3481  */
3482 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3483 Roo.Msg = Roo.Msg || Roo.MessageBox;
3484 /*
3485  * - LGPL
3486  *
3487  * navbar
3488  * 
3489  */
3490
3491 /**
3492  * @class Roo.bootstrap.Navbar
3493  * @extends Roo.bootstrap.Component
3494  * Bootstrap Navbar class
3495
3496  * @constructor
3497  * Create a new Navbar
3498  * @param {Object} config The config object
3499  */
3500
3501
3502 Roo.bootstrap.Navbar = function(config){
3503     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3504     this.addEvents({
3505         // raw events
3506         /**
3507          * @event beforetoggle
3508          * Fire before toggle the menu
3509          * @param {Roo.EventObject} e
3510          */
3511         "beforetoggle" : true
3512     });
3513 };
3514
3515 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3516     
3517     
3518    
3519     // private
3520     navItems : false,
3521     loadMask : false,
3522     
3523     
3524     getAutoCreate : function(){
3525         
3526         
3527         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3528         
3529     },
3530     
3531     initEvents :function ()
3532     {
3533         //Roo.log(this.el.select('.navbar-toggle',true));
3534         this.el.select('.navbar-toggle',true).on('click', function() {
3535             if(this.fireEvent('beforetoggle', this) !== false){
3536                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3537             }
3538             
3539         }, this);
3540         
3541         var mark = {
3542             tag: "div",
3543             cls:"x-dlg-mask"
3544         };
3545         
3546         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3547         
3548         var size = this.el.getSize();
3549         this.maskEl.setSize(size.width, size.height);
3550         this.maskEl.enableDisplayMode("block");
3551         this.maskEl.hide();
3552         
3553         if(this.loadMask){
3554             this.maskEl.show();
3555         }
3556     },
3557     
3558     
3559     getChildContainer : function()
3560     {
3561         if (this.el.select('.collapse').getCount()) {
3562             return this.el.select('.collapse',true).first();
3563         }
3564         
3565         return this.el;
3566     },
3567     
3568     mask : function()
3569     {
3570         this.maskEl.show();
3571     },
3572     
3573     unmask : function()
3574     {
3575         this.maskEl.hide();
3576     } 
3577     
3578     
3579     
3580     
3581 });
3582
3583
3584
3585  
3586
3587  /*
3588  * - LGPL
3589  *
3590  * navbar
3591  * 
3592  */
3593
3594 /**
3595  * @class Roo.bootstrap.NavSimplebar
3596  * @extends Roo.bootstrap.Navbar
3597  * Bootstrap Sidebar class
3598  *
3599  * @cfg {Boolean} inverse is inverted color
3600  * 
3601  * @cfg {String} type (nav | pills | tabs)
3602  * @cfg {Boolean} arrangement stacked | justified
3603  * @cfg {String} align (left | right) alignment
3604  * 
3605  * @cfg {Boolean} main (true|false) main nav bar? default false
3606  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3607  * 
3608  * @cfg {String} tag (header|footer|nav|div) default is nav 
3609
3610  * 
3611  * 
3612  * 
3613  * @constructor
3614  * Create a new Sidebar
3615  * @param {Object} config The config object
3616  */
3617
3618
3619 Roo.bootstrap.NavSimplebar = function(config){
3620     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3621 };
3622
3623 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3624     
3625     inverse: false,
3626     
3627     type: false,
3628     arrangement: '',
3629     align : false,
3630     
3631     
3632     
3633     main : false,
3634     
3635     
3636     tag : false,
3637     
3638     
3639     getAutoCreate : function(){
3640         
3641         
3642         var cfg = {
3643             tag : this.tag || 'div',
3644             cls : 'navbar'
3645         };
3646           
3647         
3648         cfg.cn = [
3649             {
3650                 cls: 'nav',
3651                 tag : 'ul'
3652             }
3653         ];
3654         
3655          
3656         this.type = this.type || 'nav';
3657         if (['tabs','pills'].indexOf(this.type)!==-1) {
3658             cfg.cn[0].cls += ' nav-' + this.type
3659         
3660         
3661         } else {
3662             if (this.type!=='nav') {
3663                 Roo.log('nav type must be nav/tabs/pills')
3664             }
3665             cfg.cn[0].cls += ' navbar-nav'
3666         }
3667         
3668         
3669         
3670         
3671         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3672             cfg.cn[0].cls += ' nav-' + this.arrangement;
3673         }
3674         
3675         
3676         if (this.align === 'right') {
3677             cfg.cn[0].cls += ' navbar-right';
3678         }
3679         
3680         if (this.inverse) {
3681             cfg.cls += ' navbar-inverse';
3682             
3683         }
3684         
3685         
3686         return cfg;
3687     
3688         
3689     }
3690     
3691     
3692     
3693 });
3694
3695
3696
3697  
3698
3699  
3700        /*
3701  * - LGPL
3702  *
3703  * navbar
3704  * 
3705  */
3706
3707 /**
3708  * @class Roo.bootstrap.NavHeaderbar
3709  * @extends Roo.bootstrap.NavSimplebar
3710  * Bootstrap Sidebar class
3711  *
3712  * @cfg {String} brand what is brand
3713  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3714  * @cfg {String} brand_href href of the brand
3715  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3716  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3717  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3718  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3719  * 
3720  * @constructor
3721  * Create a new Sidebar
3722  * @param {Object} config The config object
3723  */
3724
3725
3726 Roo.bootstrap.NavHeaderbar = function(config){
3727     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3728       
3729 };
3730
3731 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3732     
3733     position: '',
3734     brand: '',
3735     brand_href: false,
3736     srButton : true,
3737     autohide : false,
3738     desktopCenter : false,
3739    
3740     
3741     getAutoCreate : function(){
3742         
3743         var   cfg = {
3744             tag: this.nav || 'nav',
3745             cls: 'navbar',
3746             role: 'navigation',
3747             cn: []
3748         };
3749         
3750         var cn = cfg.cn;
3751         if (this.desktopCenter) {
3752             cn.push({cls : 'container', cn : []});
3753             cn = cn[0].cn;
3754         }
3755         
3756         if(this.srButton){
3757             cn.push({
3758                 tag: 'div',
3759                 cls: 'navbar-header',
3760                 cn: [
3761                     {
3762                         tag: 'button',
3763                         type: 'button',
3764                         cls: 'navbar-toggle',
3765                         'data-toggle': 'collapse',
3766                         cn: [
3767                             {
3768                                 tag: 'span',
3769                                 cls: 'sr-only',
3770                                 html: 'Toggle navigation'
3771                             },
3772                             {
3773                                 tag: 'span',
3774                                 cls: 'icon-bar'
3775                             },
3776                             {
3777                                 tag: 'span',
3778                                 cls: 'icon-bar'
3779                             },
3780                             {
3781                                 tag: 'span',
3782                                 cls: 'icon-bar'
3783                             }
3784                         ]
3785                     }
3786                 ]
3787             });
3788         }
3789         
3790         cn.push({
3791             tag: 'div',
3792             cls: 'collapse navbar-collapse',
3793             cn : []
3794         });
3795         
3796         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3797         
3798         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3799             cfg.cls += ' navbar-' + this.position;
3800             
3801             // tag can override this..
3802             
3803             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3804         }
3805         
3806         if (this.brand !== '') {
3807             cn[0].cn.push({
3808                 tag: 'a',
3809                 href: this.brand_href ? this.brand_href : '#',
3810                 cls: 'navbar-brand',
3811                 cn: [
3812                 this.brand
3813                 ]
3814             });
3815         }
3816         
3817         if(this.main){
3818             cfg.cls += ' main-nav';
3819         }
3820         
3821         
3822         return cfg;
3823
3824         
3825     },
3826     getHeaderChildContainer : function()
3827     {
3828         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3829             return this.el.select('.navbar-header',true).first();
3830         }
3831         
3832         return this.getChildContainer();
3833     },
3834     
3835     
3836     initEvents : function()
3837     {
3838         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3839         
3840         if (this.autohide) {
3841             
3842             var prevScroll = 0;
3843             var ft = this.el;
3844             
3845             Roo.get(document).on('scroll',function(e) {
3846                 var ns = Roo.get(document).getScroll().top;
3847                 var os = prevScroll;
3848                 prevScroll = ns;
3849                 
3850                 if(ns > os){
3851                     ft.removeClass('slideDown');
3852                     ft.addClass('slideUp');
3853                     return;
3854                 }
3855                 ft.removeClass('slideUp');
3856                 ft.addClass('slideDown');
3857                  
3858               
3859           },this);
3860         }
3861     }    
3862     
3863 });
3864
3865
3866
3867  
3868
3869  /*
3870  * - LGPL
3871  *
3872  * navbar
3873  * 
3874  */
3875
3876 /**
3877  * @class Roo.bootstrap.NavSidebar
3878  * @extends Roo.bootstrap.Navbar
3879  * Bootstrap Sidebar class
3880  * 
3881  * @constructor
3882  * Create a new Sidebar
3883  * @param {Object} config The config object
3884  */
3885
3886
3887 Roo.bootstrap.NavSidebar = function(config){
3888     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3889 };
3890
3891 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3892     
3893     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3894     
3895     getAutoCreate : function(){
3896         
3897         
3898         return  {
3899             tag: 'div',
3900             cls: 'sidebar sidebar-nav'
3901         };
3902     
3903         
3904     }
3905     
3906     
3907     
3908 });
3909
3910
3911
3912  
3913
3914  /*
3915  * - LGPL
3916  *
3917  * nav group
3918  * 
3919  */
3920
3921 /**
3922  * @class Roo.bootstrap.NavGroup
3923  * @extends Roo.bootstrap.Component
3924  * Bootstrap NavGroup class
3925  * @cfg {String} align (left|right)
3926  * @cfg {Boolean} inverse
3927  * @cfg {String} type (nav|pills|tab) default nav
3928  * @cfg {String} navId - reference Id for navbar.
3929
3930  * 
3931  * @constructor
3932  * Create a new nav group
3933  * @param {Object} config The config object
3934  */
3935
3936 Roo.bootstrap.NavGroup = function(config){
3937     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3938     this.navItems = [];
3939    
3940     Roo.bootstrap.NavGroup.register(this);
3941      this.addEvents({
3942         /**
3943              * @event changed
3944              * Fires when the active item changes
3945              * @param {Roo.bootstrap.NavGroup} this
3946              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3947              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3948          */
3949         'changed': true
3950      });
3951     
3952 };
3953
3954 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3955     
3956     align: '',
3957     inverse: false,
3958     form: false,
3959     type: 'nav',
3960     navId : '',
3961     // private
3962     
3963     navItems : false, 
3964     
3965     getAutoCreate : function()
3966     {
3967         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3968         
3969         cfg = {
3970             tag : 'ul',
3971             cls: 'nav' 
3972         };
3973         
3974         if (['tabs','pills'].indexOf(this.type)!==-1) {
3975             cfg.cls += ' nav-' + this.type
3976         } else {
3977             if (this.type!=='nav') {
3978                 Roo.log('nav type must be nav/tabs/pills')
3979             }
3980             cfg.cls += ' navbar-nav'
3981         }
3982         
3983         if (this.parent().sidebar) {
3984             cfg = {
3985                 tag: 'ul',
3986                 cls: 'dashboard-menu sidebar-menu'
3987             };
3988             
3989             return cfg;
3990         }
3991         
3992         if (this.form === true) {
3993             cfg = {
3994                 tag: 'form',
3995                 cls: 'navbar-form'
3996             };
3997             
3998             if (this.align === 'right') {
3999                 cfg.cls += ' navbar-right';
4000             } else {
4001                 cfg.cls += ' navbar-left';
4002             }
4003         }
4004         
4005         if (this.align === 'right') {
4006             cfg.cls += ' navbar-right';
4007         }
4008         
4009         if (this.inverse) {
4010             cfg.cls += ' navbar-inverse';
4011             
4012         }
4013         
4014         
4015         return cfg;
4016     },
4017     /**
4018     * sets the active Navigation item
4019     * @param {Roo.bootstrap.NavItem} the new current navitem
4020     */
4021     setActiveItem : function(item)
4022     {
4023         var prev = false;
4024         Roo.each(this.navItems, function(v){
4025             if (v == item) {
4026                 return ;
4027             }
4028             if (v.isActive()) {
4029                 v.setActive(false, true);
4030                 prev = v;
4031                 
4032             }
4033             
4034         });
4035
4036         item.setActive(true, true);
4037         this.fireEvent('changed', this, item, prev);
4038         
4039         
4040     },
4041     /**
4042     * gets the active Navigation item
4043     * @return {Roo.bootstrap.NavItem} the current navitem
4044     */
4045     getActive : function()
4046     {
4047         
4048         var prev = false;
4049         Roo.each(this.navItems, function(v){
4050             
4051             if (v.isActive()) {
4052                 prev = v;
4053                 
4054             }
4055             
4056         });
4057         return prev;
4058     },
4059     
4060     indexOfNav : function()
4061     {
4062         
4063         var prev = false;
4064         Roo.each(this.navItems, function(v,i){
4065             
4066             if (v.isActive()) {
4067                 prev = i;
4068                 
4069             }
4070             
4071         });
4072         return prev;
4073     },
4074     /**
4075     * adds a Navigation item
4076     * @param {Roo.bootstrap.NavItem} the navitem to add
4077     */
4078     addItem : function(cfg)
4079     {
4080         var cn = new Roo.bootstrap.NavItem(cfg);
4081         this.register(cn);
4082         cn.parentId = this.id;
4083         cn.onRender(this.el, null);
4084         return cn;
4085     },
4086     /**
4087     * register a Navigation item
4088     * @param {Roo.bootstrap.NavItem} the navitem to add
4089     */
4090     register : function(item)
4091     {
4092         this.navItems.push( item);
4093         item.navId = this.navId;
4094     
4095     },
4096     
4097     /**
4098     * clear all the Navigation item
4099     */
4100    
4101     clearAll : function()
4102     {
4103         this.navItems = [];
4104         this.el.dom.innerHTML = '';
4105     },
4106     
4107     getNavItem: function(tabId)
4108     {
4109         var ret = false;
4110         Roo.each(this.navItems, function(e) {
4111             if (e.tabId == tabId) {
4112                ret =  e;
4113                return false;
4114             }
4115             return true;
4116             
4117         });
4118         return ret;
4119     },
4120     
4121     setActiveNext : function()
4122     {
4123         var i = this.indexOfNav(this.getActive());
4124         if (i > this.navItems.length) {
4125             return;
4126         }
4127         this.setActiveItem(this.navItems[i+1]);
4128     },
4129     setActivePrev : function()
4130     {
4131         var i = this.indexOfNav(this.getActive());
4132         if (i  < 1) {
4133             return;
4134         }
4135         this.setActiveItem(this.navItems[i-1]);
4136     },
4137     clearWasActive : function(except) {
4138         Roo.each(this.navItems, function(e) {
4139             if (e.tabId != except.tabId && e.was_active) {
4140                e.was_active = false;
4141                return false;
4142             }
4143             return true;
4144             
4145         });
4146     },
4147     getWasActive : function ()
4148     {
4149         var r = false;
4150         Roo.each(this.navItems, function(e) {
4151             if (e.was_active) {
4152                r = e;
4153                return false;
4154             }
4155             return true;
4156             
4157         });
4158         return r;
4159     }
4160     
4161     
4162 });
4163
4164  
4165 Roo.apply(Roo.bootstrap.NavGroup, {
4166     
4167     groups: {},
4168      /**
4169     * register a Navigation Group
4170     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4171     */
4172     register : function(navgrp)
4173     {
4174         this.groups[navgrp.navId] = navgrp;
4175         
4176     },
4177     /**
4178     * fetch a Navigation Group based on the navigation ID
4179     * @param {string} the navgroup to add
4180     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4181     */
4182     get: function(navId) {
4183         if (typeof(this.groups[navId]) == 'undefined') {
4184             return false;
4185             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4186         }
4187         return this.groups[navId] ;
4188     }
4189     
4190     
4191     
4192 });
4193
4194  /*
4195  * - LGPL
4196  *
4197  * row
4198  * 
4199  */
4200
4201 /**
4202  * @class Roo.bootstrap.NavItem
4203  * @extends Roo.bootstrap.Component
4204  * Bootstrap Navbar.NavItem class
4205  * @cfg {String} href  link to
4206  * @cfg {String} html content of button
4207  * @cfg {String} badge text inside badge
4208  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4209  * @cfg {String} glyphicon name of glyphicon
4210  * @cfg {String} icon name of font awesome icon
4211  * @cfg {Boolean} active Is item active
4212  * @cfg {Boolean} disabled Is item disabled
4213  
4214  * @cfg {Boolean} preventDefault (true | false) default false
4215  * @cfg {String} tabId the tab that this item activates.
4216  * @cfg {String} tagtype (a|span) render as a href or span?
4217  * @cfg {Boolean} animateRef (true|false) link to element default false  
4218   
4219  * @constructor
4220  * Create a new Navbar Item
4221  * @param {Object} config The config object
4222  */
4223 Roo.bootstrap.NavItem = function(config){
4224     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4225     this.addEvents({
4226         // raw events
4227         /**
4228          * @event click
4229          * The raw click event for the entire grid.
4230          * @param {Roo.EventObject} e
4231          */
4232         "click" : true,
4233          /**
4234             * @event changed
4235             * Fires when the active item active state changes
4236             * @param {Roo.bootstrap.NavItem} this
4237             * @param {boolean} state the new state
4238              
4239          */
4240         'changed': true,
4241         /**
4242             * @event scrollto
4243             * Fires when scroll to element
4244             * @param {Roo.bootstrap.NavItem} this
4245             * @param {Object} options
4246             * @param {Roo.EventObject} e
4247              
4248          */
4249         'scrollto': true
4250     });
4251    
4252 };
4253
4254 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4255     
4256     href: false,
4257     html: '',
4258     badge: '',
4259     icon: false,
4260     glyphicon: false,
4261     active: false,
4262     preventDefault : false,
4263     tabId : false,
4264     tagtype : 'a',
4265     disabled : false,
4266     animateRef : false,
4267     was_active : false,
4268     
4269     getAutoCreate : function(){
4270          
4271         var cfg = {
4272             tag: 'li',
4273             cls: 'nav-item'
4274             
4275         };
4276         
4277         if (this.active) {
4278             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4279         }
4280         if (this.disabled) {
4281             cfg.cls += ' disabled';
4282         }
4283         
4284         if (this.href || this.html || this.glyphicon || this.icon) {
4285             cfg.cn = [
4286                 {
4287                     tag: this.tagtype,
4288                     href : this.href || "#",
4289                     html: this.html || ''
4290                 }
4291             ];
4292             
4293             if (this.icon) {
4294                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4295             }
4296
4297             if(this.glyphicon) {
4298                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4299             }
4300             
4301             if (this.menu) {
4302                 
4303                 cfg.cn[0].html += " <span class='caret'></span>";
4304              
4305             }
4306             
4307             if (this.badge !== '') {
4308                  
4309                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4310             }
4311         }
4312         
4313         
4314         
4315         return cfg;
4316     },
4317     initEvents: function() 
4318     {
4319         if (typeof (this.menu) != 'undefined') {
4320             this.menu.parentType = this.xtype;
4321             this.menu.triggerEl = this.el;
4322             this.menu = this.addxtype(Roo.apply({}, this.menu));
4323         }
4324         
4325         this.el.select('a',true).on('click', this.onClick, this);
4326         
4327         if(this.tagtype == 'span'){
4328             this.el.select('span',true).on('click', this.onClick, this);
4329         }
4330        
4331         // at this point parent should be available..
4332         this.parent().register(this);
4333     },
4334     
4335     onClick : function(e)
4336     {
4337         if (e.getTarget('.dropdown-menu-item')) {
4338             // did you click on a menu itemm.... - then don't trigger onclick..
4339             return;
4340         }
4341         
4342         if(
4343                 this.preventDefault || 
4344                 this.href == '#' 
4345         ){
4346             Roo.log("NavItem - prevent Default?");
4347             e.preventDefault();
4348         }
4349         
4350         if (this.disabled) {
4351             return;
4352         }
4353         
4354         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4355         if (tg && tg.transition) {
4356             Roo.log("waiting for the transitionend");
4357             return;
4358         }
4359         
4360         
4361         
4362         //Roo.log("fire event clicked");
4363         if(this.fireEvent('click', this, e) === false){
4364             return;
4365         };
4366         
4367         if(this.tagtype == 'span'){
4368             return;
4369         }
4370         
4371         //Roo.log(this.href);
4372         var ael = this.el.select('a',true).first();
4373         //Roo.log(ael);
4374         
4375         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4376             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4377             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4378                 return; // ignore... - it's a 'hash' to another page.
4379             }
4380             Roo.log("NavItem - prevent Default?");
4381             e.preventDefault();
4382             this.scrollToElement(e);
4383         }
4384         
4385         
4386         var p =  this.parent();
4387    
4388         if (['tabs','pills'].indexOf(p.type)!==-1) {
4389             if (typeof(p.setActiveItem) !== 'undefined') {
4390                 p.setActiveItem(this);
4391             }
4392         }
4393         
4394         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4395         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4396             // remove the collapsed menu expand...
4397             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4398         }
4399     },
4400     
4401     isActive: function () {
4402         return this.active
4403     },
4404     setActive : function(state, fire, is_was_active)
4405     {
4406         if (this.active && !state && this.navId) {
4407             this.was_active = true;
4408             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4409             if (nv) {
4410                 nv.clearWasActive(this);
4411             }
4412             
4413         }
4414         this.active = state;
4415         
4416         if (!state ) {
4417             this.el.removeClass('active');
4418         } else if (!this.el.hasClass('active')) {
4419             this.el.addClass('active');
4420         }
4421         if (fire) {
4422             this.fireEvent('changed', this, state);
4423         }
4424         
4425         // show a panel if it's registered and related..
4426         
4427         if (!this.navId || !this.tabId || !state || is_was_active) {
4428             return;
4429         }
4430         
4431         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4432         if (!tg) {
4433             return;
4434         }
4435         var pan = tg.getPanelByName(this.tabId);
4436         if (!pan) {
4437             return;
4438         }
4439         // if we can not flip to new panel - go back to old nav highlight..
4440         if (false == tg.showPanel(pan)) {
4441             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4442             if (nv) {
4443                 var onav = nv.getWasActive();
4444                 if (onav) {
4445                     onav.setActive(true, false, true);
4446                 }
4447             }
4448             
4449         }
4450         
4451         
4452         
4453     },
4454      // this should not be here...
4455     setDisabled : function(state)
4456     {
4457         this.disabled = state;
4458         if (!state ) {
4459             this.el.removeClass('disabled');
4460         } else if (!this.el.hasClass('disabled')) {
4461             this.el.addClass('disabled');
4462         }
4463         
4464     },
4465     
4466     /**
4467      * Fetch the element to display the tooltip on.
4468      * @return {Roo.Element} defaults to this.el
4469      */
4470     tooltipEl : function()
4471     {
4472         return this.el.select('' + this.tagtype + '', true).first();
4473     },
4474     
4475     scrollToElement : function(e)
4476     {
4477         var c = document.body;
4478         
4479         /*
4480          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4481          */
4482         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4483             c = document.documentElement;
4484         }
4485         
4486         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4487         
4488         if(!target){
4489             return;
4490         }
4491
4492         var o = target.calcOffsetsTo(c);
4493         
4494         var options = {
4495             target : target,
4496             value : o[1]
4497         };
4498         
4499         this.fireEvent('scrollto', this, options, e);
4500         
4501         Roo.get(c).scrollTo('top', options.value, true);
4502         
4503         return;
4504     }
4505 });
4506  
4507
4508  /*
4509  * - LGPL
4510  *
4511  * sidebar item
4512  *
4513  *  li
4514  *    <span> icon </span>
4515  *    <span> text </span>
4516  *    <span>badge </span>
4517  */
4518
4519 /**
4520  * @class Roo.bootstrap.NavSidebarItem
4521  * @extends Roo.bootstrap.NavItem
4522  * Bootstrap Navbar.NavSidebarItem class
4523  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4524  * {bool} open is the menu open
4525  * @constructor
4526  * Create a new Navbar Button
4527  * @param {Object} config The config object
4528  */
4529 Roo.bootstrap.NavSidebarItem = function(config){
4530     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4531     this.addEvents({
4532         // raw events
4533         /**
4534          * @event click
4535          * The raw click event for the entire grid.
4536          * @param {Roo.EventObject} e
4537          */
4538         "click" : true,
4539          /**
4540             * @event changed
4541             * Fires when the active item active state changes
4542             * @param {Roo.bootstrap.NavSidebarItem} this
4543             * @param {boolean} state the new state
4544              
4545          */
4546         'changed': true
4547     });
4548    
4549 };
4550
4551 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4552     
4553     badgeWeight : 'default',
4554     
4555     open: false,
4556     
4557     getAutoCreate : function(){
4558         
4559         
4560         var a = {
4561                 tag: 'a',
4562                 href : this.href || '#',
4563                 cls: '',
4564                 html : '',
4565                 cn : []
4566         };
4567         var cfg = {
4568             tag: 'li',
4569             cls: '',
4570             cn: [ a ]
4571         };
4572         var span = {
4573             tag: 'span',
4574             html : this.html || ''
4575         };
4576         
4577         
4578         if (this.active) {
4579             cfg.cls += ' active';
4580         }
4581         
4582         if (this.disabled) {
4583             cfg.cls += ' disabled';
4584         }
4585         if (this.open) {
4586             cfg.cls += ' open x-open';
4587         }
4588         // left icon..
4589         if (this.glyphicon || this.icon) {
4590             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4591             a.cn.push({ tag : 'i', cls : c }) ;
4592         }
4593         // html..
4594         a.cn.push(span);
4595         // then badge..
4596         if (this.badge !== '') {
4597             
4598             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4599         }
4600         // fi
4601         if (this.menu) {
4602             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4603             a.cls += 'dropdown-toggle treeview' ;
4604         }
4605         
4606         return cfg;
4607          
4608            
4609     },
4610     
4611     initEvents : function()
4612     { 
4613         if (typeof (this.menu) != 'undefined') {
4614             this.menu.parentType = this.xtype;
4615             this.menu.triggerEl = this.el;
4616             this.menu = this.addxtype(Roo.apply({}, this.menu));
4617         }
4618         
4619         this.el.on('click', this.onClick, this);
4620        
4621     
4622         if(this.badge !== ''){
4623  
4624             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4625         }
4626         
4627     },
4628     
4629     onClick : function(e)
4630     {
4631         if(this.disabled){
4632             e.preventDefault();
4633             return;
4634         }
4635         
4636         if(this.preventDefault){
4637             e.preventDefault();
4638         }
4639         
4640         this.fireEvent('click', this);
4641     },
4642     
4643     disable : function()
4644     {
4645         this.setDisabled(true);
4646     },
4647     
4648     enable : function()
4649     {
4650         this.setDisabled(false);
4651     },
4652     
4653     setDisabled : function(state)
4654     {
4655         if(this.disabled == state){
4656             return;
4657         }
4658         
4659         this.disabled = state;
4660         
4661         if (state) {
4662             this.el.addClass('disabled');
4663             return;
4664         }
4665         
4666         this.el.removeClass('disabled');
4667         
4668         return;
4669     },
4670     
4671     setActive : function(state)
4672     {
4673         if(this.active == state){
4674             return;
4675         }
4676         
4677         this.active = state;
4678         
4679         if (state) {
4680             this.el.addClass('active');
4681             return;
4682         }
4683         
4684         this.el.removeClass('active');
4685         
4686         return;
4687     },
4688     
4689     isActive: function () 
4690     {
4691         return this.active;
4692     },
4693     
4694     setBadge : function(str)
4695     {
4696         if(!this.badgeEl){
4697             return;
4698         }
4699         
4700         this.badgeEl.dom.innerHTML = str;
4701     }
4702     
4703    
4704      
4705  
4706 });
4707  
4708
4709  /*
4710  * - LGPL
4711  *
4712  * row
4713  * 
4714  */
4715
4716 /**
4717  * @class Roo.bootstrap.Row
4718  * @extends Roo.bootstrap.Component
4719  * Bootstrap Row class (contains columns...)
4720  * 
4721  * @constructor
4722  * Create a new Row
4723  * @param {Object} config The config object
4724  */
4725
4726 Roo.bootstrap.Row = function(config){
4727     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4728 };
4729
4730 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4731     
4732     getAutoCreate : function(){
4733        return {
4734             cls: 'row clearfix'
4735        };
4736     }
4737     
4738     
4739 });
4740
4741  
4742
4743  /*
4744  * - LGPL
4745  *
4746  * element
4747  * 
4748  */
4749
4750 /**
4751  * @class Roo.bootstrap.Element
4752  * @extends Roo.bootstrap.Component
4753  * Bootstrap Element class
4754  * @cfg {String} html contents of the element
4755  * @cfg {String} tag tag of the element
4756  * @cfg {String} cls class of the element
4757  * @cfg {Boolean} preventDefault (true|false) default false
4758  * @cfg {Boolean} clickable (true|false) default false
4759  * 
4760  * @constructor
4761  * Create a new Element
4762  * @param {Object} config The config object
4763  */
4764
4765 Roo.bootstrap.Element = function(config){
4766     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4767     
4768     this.addEvents({
4769         // raw events
4770         /**
4771          * @event click
4772          * When a element is chick
4773          * @param {Roo.bootstrap.Element} this
4774          * @param {Roo.EventObject} e
4775          */
4776         "click" : true
4777     });
4778 };
4779
4780 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4781     
4782     tag: 'div',
4783     cls: '',
4784     html: '',
4785     preventDefault: false, 
4786     clickable: false,
4787     
4788     getAutoCreate : function(){
4789         
4790         var cfg = {
4791             tag: this.tag,
4792             cls: this.cls,
4793             html: this.html
4794         };
4795         
4796         return cfg;
4797     },
4798     
4799     initEvents: function() 
4800     {
4801         Roo.bootstrap.Element.superclass.initEvents.call(this);
4802         
4803         if(this.clickable){
4804             this.el.on('click', this.onClick, this);
4805         }
4806         
4807     },
4808     
4809     onClick : function(e)
4810     {
4811         if(this.preventDefault){
4812             e.preventDefault();
4813         }
4814         
4815         this.fireEvent('click', this, e);
4816     },
4817     
4818     getValue : function()
4819     {
4820         return this.el.dom.innerHTML;
4821     },
4822     
4823     setValue : function(value)
4824     {
4825         this.el.dom.innerHTML = value;
4826     }
4827    
4828 });
4829
4830  
4831
4832  /*
4833  * - LGPL
4834  *
4835  * pagination
4836  * 
4837  */
4838
4839 /**
4840  * @class Roo.bootstrap.Pagination
4841  * @extends Roo.bootstrap.Component
4842  * Bootstrap Pagination class
4843  * @cfg {String} size xs | sm | md | lg
4844  * @cfg {Boolean} inverse false | true
4845  * 
4846  * @constructor
4847  * Create a new Pagination
4848  * @param {Object} config The config object
4849  */
4850
4851 Roo.bootstrap.Pagination = function(config){
4852     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4853 };
4854
4855 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4856     
4857     cls: false,
4858     size: false,
4859     inverse: false,
4860     
4861     getAutoCreate : function(){
4862         var cfg = {
4863             tag: 'ul',
4864                 cls: 'pagination'
4865         };
4866         if (this.inverse) {
4867             cfg.cls += ' inverse';
4868         }
4869         if (this.html) {
4870             cfg.html=this.html;
4871         }
4872         if (this.cls) {
4873             cfg.cls += " " + this.cls;
4874         }
4875         return cfg;
4876     }
4877    
4878 });
4879
4880  
4881
4882  /*
4883  * - LGPL
4884  *
4885  * Pagination item
4886  * 
4887  */
4888
4889
4890 /**
4891  * @class Roo.bootstrap.PaginationItem
4892  * @extends Roo.bootstrap.Component
4893  * Bootstrap PaginationItem class
4894  * @cfg {String} html text
4895  * @cfg {String} href the link
4896  * @cfg {Boolean} preventDefault (true | false) default true
4897  * @cfg {Boolean} active (true | false) default false
4898  * @cfg {Boolean} disabled default false
4899  * 
4900  * 
4901  * @constructor
4902  * Create a new PaginationItem
4903  * @param {Object} config The config object
4904  */
4905
4906
4907 Roo.bootstrap.PaginationItem = function(config){
4908     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4909     this.addEvents({
4910         // raw events
4911         /**
4912          * @event click
4913          * The raw click event for the entire grid.
4914          * @param {Roo.EventObject} e
4915          */
4916         "click" : true
4917     });
4918 };
4919
4920 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4921     
4922     href : false,
4923     html : false,
4924     preventDefault: true,
4925     active : false,
4926     cls : false,
4927     disabled: false,
4928     
4929     getAutoCreate : function(){
4930         var cfg= {
4931             tag: 'li',
4932             cn: [
4933                 {
4934                     tag : 'a',
4935                     href : this.href ? this.href : '#',
4936                     html : this.html ? this.html : ''
4937                 }
4938             ]
4939         };
4940         
4941         if(this.cls){
4942             cfg.cls = this.cls;
4943         }
4944         
4945         if(this.disabled){
4946             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4947         }
4948         
4949         if(this.active){
4950             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4951         }
4952         
4953         return cfg;
4954     },
4955     
4956     initEvents: function() {
4957         
4958         this.el.on('click', this.onClick, this);
4959         
4960     },
4961     onClick : function(e)
4962     {
4963         Roo.log('PaginationItem on click ');
4964         if(this.preventDefault){
4965             e.preventDefault();
4966         }
4967         
4968         if(this.disabled){
4969             return;
4970         }
4971         
4972         this.fireEvent('click', this, e);
4973     }
4974    
4975 });
4976
4977  
4978
4979  /*
4980  * - LGPL
4981  *
4982  * slider
4983  * 
4984  */
4985
4986
4987 /**
4988  * @class Roo.bootstrap.Slider
4989  * @extends Roo.bootstrap.Component
4990  * Bootstrap Slider class
4991  *    
4992  * @constructor
4993  * Create a new Slider
4994  * @param {Object} config The config object
4995  */
4996
4997 Roo.bootstrap.Slider = function(config){
4998     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4999 };
5000
5001 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5002     
5003     getAutoCreate : function(){
5004         
5005         var cfg = {
5006             tag: 'div',
5007             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5008             cn: [
5009                 {
5010                     tag: 'a',
5011                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5012                 }
5013             ]
5014         };
5015         
5016         return cfg;
5017     }
5018    
5019 });
5020
5021  /*
5022  * Based on:
5023  * Ext JS Library 1.1.1
5024  * Copyright(c) 2006-2007, Ext JS, LLC.
5025  *
5026  * Originally Released Under LGPL - original licence link has changed is not relivant.
5027  *
5028  * Fork - LGPL
5029  * <script type="text/javascript">
5030  */
5031  
5032
5033 /**
5034  * @class Roo.grid.ColumnModel
5035  * @extends Roo.util.Observable
5036  * This is the default implementation of a ColumnModel used by the Grid. It defines
5037  * the columns in the grid.
5038  * <br>Usage:<br>
5039  <pre><code>
5040  var colModel = new Roo.grid.ColumnModel([
5041         {header: "Ticker", width: 60, sortable: true, locked: true},
5042         {header: "Company Name", width: 150, sortable: true},
5043         {header: "Market Cap.", width: 100, sortable: true},
5044         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5045         {header: "Employees", width: 100, sortable: true, resizable: false}
5046  ]);
5047  </code></pre>
5048  * <p>
5049  
5050  * The config options listed for this class are options which may appear in each
5051  * individual column definition.
5052  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5053  * @constructor
5054  * @param {Object} config An Array of column config objects. See this class's
5055  * config objects for details.
5056 */
5057 Roo.grid.ColumnModel = function(config){
5058         /**
5059      * The config passed into the constructor
5060      */
5061     this.config = config;
5062     this.lookup = {};
5063
5064     // if no id, create one
5065     // if the column does not have a dataIndex mapping,
5066     // map it to the order it is in the config
5067     for(var i = 0, len = config.length; i < len; i++){
5068         var c = config[i];
5069         if(typeof c.dataIndex == "undefined"){
5070             c.dataIndex = i;
5071         }
5072         if(typeof c.renderer == "string"){
5073             c.renderer = Roo.util.Format[c.renderer];
5074         }
5075         if(typeof c.id == "undefined"){
5076             c.id = Roo.id();
5077         }
5078         if(c.editor && c.editor.xtype){
5079             c.editor  = Roo.factory(c.editor, Roo.grid);
5080         }
5081         if(c.editor && c.editor.isFormField){
5082             c.editor = new Roo.grid.GridEditor(c.editor);
5083         }
5084         this.lookup[c.id] = c;
5085     }
5086
5087     /**
5088      * The width of columns which have no width specified (defaults to 100)
5089      * @type Number
5090      */
5091     this.defaultWidth = 100;
5092
5093     /**
5094      * Default sortable of columns which have no sortable specified (defaults to false)
5095      * @type Boolean
5096      */
5097     this.defaultSortable = false;
5098
5099     this.addEvents({
5100         /**
5101              * @event widthchange
5102              * Fires when the width of a column changes.
5103              * @param {ColumnModel} this
5104              * @param {Number} columnIndex The column index
5105              * @param {Number} newWidth The new width
5106              */
5107             "widthchange": true,
5108         /**
5109              * @event headerchange
5110              * Fires when the text of a header changes.
5111              * @param {ColumnModel} this
5112              * @param {Number} columnIndex The column index
5113              * @param {Number} newText The new header text
5114              */
5115             "headerchange": true,
5116         /**
5117              * @event hiddenchange
5118              * Fires when a column is hidden or "unhidden".
5119              * @param {ColumnModel} this
5120              * @param {Number} columnIndex The column index
5121              * @param {Boolean} hidden true if hidden, false otherwise
5122              */
5123             "hiddenchange": true,
5124             /**
5125          * @event columnmoved
5126          * Fires when a column is moved.
5127          * @param {ColumnModel} this
5128          * @param {Number} oldIndex
5129          * @param {Number} newIndex
5130          */
5131         "columnmoved" : true,
5132         /**
5133          * @event columlockchange
5134          * Fires when a column's locked state is changed
5135          * @param {ColumnModel} this
5136          * @param {Number} colIndex
5137          * @param {Boolean} locked true if locked
5138          */
5139         "columnlockchange" : true
5140     });
5141     Roo.grid.ColumnModel.superclass.constructor.call(this);
5142 };
5143 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5144     /**
5145      * @cfg {String} header The header text to display in the Grid view.
5146      */
5147     /**
5148      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5149      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5150      * specified, the column's index is used as an index into the Record's data Array.
5151      */
5152     /**
5153      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5154      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5155      */
5156     /**
5157      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5158      * Defaults to the value of the {@link #defaultSortable} property.
5159      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5160      */
5161     /**
5162      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5163      */
5164     /**
5165      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5166      */
5167     /**
5168      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5169      */
5170     /**
5171      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5172      */
5173     /**
5174      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5175      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5176      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5177      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5178      */
5179        /**
5180      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5181      */
5182     /**
5183      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5184      */
5185     /**
5186      * @cfg {String} cursor (Optional)
5187      */
5188     /**
5189      * @cfg {String} tooltip (Optional)
5190      */
5191     /**
5192      * @cfg {Number} xs (Optional)
5193      */
5194     /**
5195      * @cfg {Number} sm (Optional)
5196      */
5197     /**
5198      * @cfg {Number} md (Optional)
5199      */
5200     /**
5201      * @cfg {Number} lg (Optional)
5202      */
5203     /**
5204      * Returns the id of the column at the specified index.
5205      * @param {Number} index The column index
5206      * @return {String} the id
5207      */
5208     getColumnId : function(index){
5209         return this.config[index].id;
5210     },
5211
5212     /**
5213      * Returns the column for a specified id.
5214      * @param {String} id The column id
5215      * @return {Object} the column
5216      */
5217     getColumnById : function(id){
5218         return this.lookup[id];
5219     },
5220
5221     
5222     /**
5223      * Returns the column for a specified dataIndex.
5224      * @param {String} dataIndex The column dataIndex
5225      * @return {Object|Boolean} the column or false if not found
5226      */
5227     getColumnByDataIndex: function(dataIndex){
5228         var index = this.findColumnIndex(dataIndex);
5229         return index > -1 ? this.config[index] : false;
5230     },
5231     
5232     /**
5233      * Returns the index for a specified column id.
5234      * @param {String} id The column id
5235      * @return {Number} the index, or -1 if not found
5236      */
5237     getIndexById : function(id){
5238         for(var i = 0, len = this.config.length; i < len; i++){
5239             if(this.config[i].id == id){
5240                 return i;
5241             }
5242         }
5243         return -1;
5244     },
5245     
5246     /**
5247      * Returns the index for a specified column dataIndex.
5248      * @param {String} dataIndex The column dataIndex
5249      * @return {Number} the index, or -1 if not found
5250      */
5251     
5252     findColumnIndex : function(dataIndex){
5253         for(var i = 0, len = this.config.length; i < len; i++){
5254             if(this.config[i].dataIndex == dataIndex){
5255                 return i;
5256             }
5257         }
5258         return -1;
5259     },
5260     
5261     
5262     moveColumn : function(oldIndex, newIndex){
5263         var c = this.config[oldIndex];
5264         this.config.splice(oldIndex, 1);
5265         this.config.splice(newIndex, 0, c);
5266         this.dataMap = null;
5267         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5268     },
5269
5270     isLocked : function(colIndex){
5271         return this.config[colIndex].locked === true;
5272     },
5273
5274     setLocked : function(colIndex, value, suppressEvent){
5275         if(this.isLocked(colIndex) == value){
5276             return;
5277         }
5278         this.config[colIndex].locked = value;
5279         if(!suppressEvent){
5280             this.fireEvent("columnlockchange", this, colIndex, value);
5281         }
5282     },
5283
5284     getTotalLockedWidth : function(){
5285         var totalWidth = 0;
5286         for(var i = 0; i < this.config.length; i++){
5287             if(this.isLocked(i) && !this.isHidden(i)){
5288                 this.totalWidth += this.getColumnWidth(i);
5289             }
5290         }
5291         return totalWidth;
5292     },
5293
5294     getLockedCount : function(){
5295         for(var i = 0, len = this.config.length; i < len; i++){
5296             if(!this.isLocked(i)){
5297                 return i;
5298             }
5299         }
5300         
5301         return this.config.length;
5302     },
5303
5304     /**
5305      * Returns the number of columns.
5306      * @return {Number}
5307      */
5308     getColumnCount : function(visibleOnly){
5309         if(visibleOnly === true){
5310             var c = 0;
5311             for(var i = 0, len = this.config.length; i < len; i++){
5312                 if(!this.isHidden(i)){
5313                     c++;
5314                 }
5315             }
5316             return c;
5317         }
5318         return this.config.length;
5319     },
5320
5321     /**
5322      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5323      * @param {Function} fn
5324      * @param {Object} scope (optional)
5325      * @return {Array} result
5326      */
5327     getColumnsBy : function(fn, scope){
5328         var r = [];
5329         for(var i = 0, len = this.config.length; i < len; i++){
5330             var c = this.config[i];
5331             if(fn.call(scope||this, c, i) === true){
5332                 r[r.length] = c;
5333             }
5334         }
5335         return r;
5336     },
5337
5338     /**
5339      * Returns true if the specified column is sortable.
5340      * @param {Number} col The column index
5341      * @return {Boolean}
5342      */
5343     isSortable : function(col){
5344         if(typeof this.config[col].sortable == "undefined"){
5345             return this.defaultSortable;
5346         }
5347         return this.config[col].sortable;
5348     },
5349
5350     /**
5351      * Returns the rendering (formatting) function defined for the column.
5352      * @param {Number} col The column index.
5353      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5354      */
5355     getRenderer : function(col){
5356         if(!this.config[col].renderer){
5357             return Roo.grid.ColumnModel.defaultRenderer;
5358         }
5359         return this.config[col].renderer;
5360     },
5361
5362     /**
5363      * Sets the rendering (formatting) function for a column.
5364      * @param {Number} col The column index
5365      * @param {Function} fn The function to use to process the cell's raw data
5366      * to return HTML markup for the grid view. The render function is called with
5367      * the following parameters:<ul>
5368      * <li>Data value.</li>
5369      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5370      * <li>css A CSS style string to apply to the table cell.</li>
5371      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5372      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5373      * <li>Row index</li>
5374      * <li>Column index</li>
5375      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5376      */
5377     setRenderer : function(col, fn){
5378         this.config[col].renderer = fn;
5379     },
5380
5381     /**
5382      * Returns the width for the specified column.
5383      * @param {Number} col The column index
5384      * @return {Number}
5385      */
5386     getColumnWidth : function(col){
5387         return this.config[col].width * 1 || this.defaultWidth;
5388     },
5389
5390     /**
5391      * Sets the width for a column.
5392      * @param {Number} col The column index
5393      * @param {Number} width The new width
5394      */
5395     setColumnWidth : function(col, width, suppressEvent){
5396         this.config[col].width = width;
5397         this.totalWidth = null;
5398         if(!suppressEvent){
5399              this.fireEvent("widthchange", this, col, width);
5400         }
5401     },
5402
5403     /**
5404      * Returns the total width of all columns.
5405      * @param {Boolean} includeHidden True to include hidden column widths
5406      * @return {Number}
5407      */
5408     getTotalWidth : function(includeHidden){
5409         if(!this.totalWidth){
5410             this.totalWidth = 0;
5411             for(var i = 0, len = this.config.length; i < len; i++){
5412                 if(includeHidden || !this.isHidden(i)){
5413                     this.totalWidth += this.getColumnWidth(i);
5414                 }
5415             }
5416         }
5417         return this.totalWidth;
5418     },
5419
5420     /**
5421      * Returns the header for the specified column.
5422      * @param {Number} col The column index
5423      * @return {String}
5424      */
5425     getColumnHeader : function(col){
5426         return this.config[col].header;
5427     },
5428
5429     /**
5430      * Sets the header for a column.
5431      * @param {Number} col The column index
5432      * @param {String} header The new header
5433      */
5434     setColumnHeader : function(col, header){
5435         this.config[col].header = header;
5436         this.fireEvent("headerchange", this, col, header);
5437     },
5438
5439     /**
5440      * Returns the tooltip for the specified column.
5441      * @param {Number} col The column index
5442      * @return {String}
5443      */
5444     getColumnTooltip : function(col){
5445             return this.config[col].tooltip;
5446     },
5447     /**
5448      * Sets the tooltip for a column.
5449      * @param {Number} col The column index
5450      * @param {String} tooltip The new tooltip
5451      */
5452     setColumnTooltip : function(col, tooltip){
5453             this.config[col].tooltip = tooltip;
5454     },
5455
5456     /**
5457      * Returns the dataIndex for the specified column.
5458      * @param {Number} col The column index
5459      * @return {Number}
5460      */
5461     getDataIndex : function(col){
5462         return this.config[col].dataIndex;
5463     },
5464
5465     /**
5466      * Sets the dataIndex for a column.
5467      * @param {Number} col The column index
5468      * @param {Number} dataIndex The new dataIndex
5469      */
5470     setDataIndex : function(col, dataIndex){
5471         this.config[col].dataIndex = dataIndex;
5472     },
5473
5474     
5475     
5476     /**
5477      * Returns true if the cell is editable.
5478      * @param {Number} colIndex The column index
5479      * @param {Number} rowIndex The row index - this is nto actually used..?
5480      * @return {Boolean}
5481      */
5482     isCellEditable : function(colIndex, rowIndex){
5483         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5484     },
5485
5486     /**
5487      * Returns the editor defined for the cell/column.
5488      * return false or null to disable editing.
5489      * @param {Number} colIndex The column index
5490      * @param {Number} rowIndex The row index
5491      * @return {Object}
5492      */
5493     getCellEditor : function(colIndex, rowIndex){
5494         return this.config[colIndex].editor;
5495     },
5496
5497     /**
5498      * Sets if a column is editable.
5499      * @param {Number} col The column index
5500      * @param {Boolean} editable True if the column is editable
5501      */
5502     setEditable : function(col, editable){
5503         this.config[col].editable = editable;
5504     },
5505
5506
5507     /**
5508      * Returns true if the column is hidden.
5509      * @param {Number} colIndex The column index
5510      * @return {Boolean}
5511      */
5512     isHidden : function(colIndex){
5513         return this.config[colIndex].hidden;
5514     },
5515
5516
5517     /**
5518      * Returns true if the column width cannot be changed
5519      */
5520     isFixed : function(colIndex){
5521         return this.config[colIndex].fixed;
5522     },
5523
5524     /**
5525      * Returns true if the column can be resized
5526      * @return {Boolean}
5527      */
5528     isResizable : function(colIndex){
5529         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5530     },
5531     /**
5532      * Sets if a column is hidden.
5533      * @param {Number} colIndex The column index
5534      * @param {Boolean} hidden True if the column is hidden
5535      */
5536     setHidden : function(colIndex, hidden){
5537         this.config[colIndex].hidden = hidden;
5538         this.totalWidth = null;
5539         this.fireEvent("hiddenchange", this, colIndex, hidden);
5540     },
5541
5542     /**
5543      * Sets the editor for a column.
5544      * @param {Number} col The column index
5545      * @param {Object} editor The editor object
5546      */
5547     setEditor : function(col, editor){
5548         this.config[col].editor = editor;
5549     }
5550 });
5551
5552 Roo.grid.ColumnModel.defaultRenderer = function(value)
5553 {
5554     if(typeof value == "object") {
5555         return value;
5556     }
5557         if(typeof value == "string" && value.length < 1){
5558             return "&#160;";
5559         }
5560     
5561         return String.format("{0}", value);
5562 };
5563
5564 // Alias for backwards compatibility
5565 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5566 /*
5567  * Based on:
5568  * Ext JS Library 1.1.1
5569  * Copyright(c) 2006-2007, Ext JS, LLC.
5570  *
5571  * Originally Released Under LGPL - original licence link has changed is not relivant.
5572  *
5573  * Fork - LGPL
5574  * <script type="text/javascript">
5575  */
5576  
5577 /**
5578  * @class Roo.LoadMask
5579  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5580  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5581  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5582  * element's UpdateManager load indicator and will be destroyed after the initial load.
5583  * @constructor
5584  * Create a new LoadMask
5585  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5586  * @param {Object} config The config object
5587  */
5588 Roo.LoadMask = function(el, config){
5589     this.el = Roo.get(el);
5590     Roo.apply(this, config);
5591     if(this.store){
5592         this.store.on('beforeload', this.onBeforeLoad, this);
5593         this.store.on('load', this.onLoad, this);
5594         this.store.on('loadexception', this.onLoadException, this);
5595         this.removeMask = false;
5596     }else{
5597         var um = this.el.getUpdateManager();
5598         um.showLoadIndicator = false; // disable the default indicator
5599         um.on('beforeupdate', this.onBeforeLoad, this);
5600         um.on('update', this.onLoad, this);
5601         um.on('failure', this.onLoad, this);
5602         this.removeMask = true;
5603     }
5604 };
5605
5606 Roo.LoadMask.prototype = {
5607     /**
5608      * @cfg {Boolean} removeMask
5609      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5610      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5611      */
5612     /**
5613      * @cfg {String} msg
5614      * The text to display in a centered loading message box (defaults to 'Loading...')
5615      */
5616     msg : 'Loading...',
5617     /**
5618      * @cfg {String} msgCls
5619      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5620      */
5621     msgCls : 'x-mask-loading',
5622
5623     /**
5624      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5625      * @type Boolean
5626      */
5627     disabled: false,
5628
5629     /**
5630      * Disables the mask to prevent it from being displayed
5631      */
5632     disable : function(){
5633        this.disabled = true;
5634     },
5635
5636     /**
5637      * Enables the mask so that it can be displayed
5638      */
5639     enable : function(){
5640         this.disabled = false;
5641     },
5642     
5643     onLoadException : function()
5644     {
5645         Roo.log(arguments);
5646         
5647         if (typeof(arguments[3]) != 'undefined') {
5648             Roo.MessageBox.alert("Error loading",arguments[3]);
5649         } 
5650         /*
5651         try {
5652             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5653                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5654             }   
5655         } catch(e) {
5656             
5657         }
5658         */
5659     
5660         
5661         
5662         this.el.unmask(this.removeMask);
5663     },
5664     // private
5665     onLoad : function()
5666     {
5667         this.el.unmask(this.removeMask);
5668     },
5669
5670     // private
5671     onBeforeLoad : function(){
5672         if(!this.disabled){
5673             this.el.mask(this.msg, this.msgCls);
5674         }
5675     },
5676
5677     // private
5678     destroy : function(){
5679         if(this.store){
5680             this.store.un('beforeload', this.onBeforeLoad, this);
5681             this.store.un('load', this.onLoad, this);
5682             this.store.un('loadexception', this.onLoadException, this);
5683         }else{
5684             var um = this.el.getUpdateManager();
5685             um.un('beforeupdate', this.onBeforeLoad, this);
5686             um.un('update', this.onLoad, this);
5687             um.un('failure', this.onLoad, this);
5688         }
5689     }
5690 };/*
5691  * - LGPL
5692  *
5693  * table
5694  * 
5695  */
5696
5697 /**
5698  * @class Roo.bootstrap.Table
5699  * @extends Roo.bootstrap.Component
5700  * Bootstrap Table class
5701  * @cfg {String} cls table class
5702  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5703  * @cfg {String} bgcolor Specifies the background color for a table
5704  * @cfg {Number} border Specifies whether the table cells should have borders or not
5705  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5706  * @cfg {Number} cellspacing Specifies the space between cells
5707  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5708  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5709  * @cfg {String} sortable Specifies that the table should be sortable
5710  * @cfg {String} summary Specifies a summary of the content of a table
5711  * @cfg {Number} width Specifies the width of a table
5712  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5713  * 
5714  * @cfg {boolean} striped Should the rows be alternative striped
5715  * @cfg {boolean} bordered Add borders to the table
5716  * @cfg {boolean} hover Add hover highlighting
5717  * @cfg {boolean} condensed Format condensed
5718  * @cfg {boolean} responsive Format condensed
5719  * @cfg {Boolean} loadMask (true|false) default false
5720  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5721  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5722  * @cfg {Boolean} rowSelection (true|false) default false
5723  * @cfg {Boolean} cellSelection (true|false) default false
5724  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5725  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5726  
5727  * 
5728  * @constructor
5729  * Create a new Table
5730  * @param {Object} config The config object
5731  */
5732
5733 Roo.bootstrap.Table = function(config){
5734     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5735     
5736   
5737     
5738     // BC...
5739     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5740     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5741     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5742     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5743     
5744     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5745     if (this.sm) {
5746         this.sm.grid = this;
5747         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5748         this.sm = this.selModel;
5749         this.sm.xmodule = this.xmodule || false;
5750     }
5751     
5752     if (this.cm && typeof(this.cm.config) == 'undefined') {
5753         this.colModel = new Roo.grid.ColumnModel(this.cm);
5754         this.cm = this.colModel;
5755         this.cm.xmodule = this.xmodule || false;
5756     }
5757     if (this.store) {
5758         this.store= Roo.factory(this.store, Roo.data);
5759         this.ds = this.store;
5760         this.ds.xmodule = this.xmodule || false;
5761          
5762     }
5763     if (this.footer && this.store) {
5764         this.footer.dataSource = this.ds;
5765         this.footer = Roo.factory(this.footer);
5766     }
5767     
5768     /** @private */
5769     this.addEvents({
5770         /**
5771          * @event cellclick
5772          * Fires when a cell is clicked
5773          * @param {Roo.bootstrap.Table} this
5774          * @param {Roo.Element} el
5775          * @param {Number} rowIndex
5776          * @param {Number} columnIndex
5777          * @param {Roo.EventObject} e
5778          */
5779         "cellclick" : true,
5780         /**
5781          * @event celldblclick
5782          * Fires when a cell is double clicked
5783          * @param {Roo.bootstrap.Table} this
5784          * @param {Roo.Element} el
5785          * @param {Number} rowIndex
5786          * @param {Number} columnIndex
5787          * @param {Roo.EventObject} e
5788          */
5789         "celldblclick" : true,
5790         /**
5791          * @event rowclick
5792          * Fires when a row is clicked
5793          * @param {Roo.bootstrap.Table} this
5794          * @param {Roo.Element} el
5795          * @param {Number} rowIndex
5796          * @param {Roo.EventObject} e
5797          */
5798         "rowclick" : true,
5799         /**
5800          * @event rowdblclick
5801          * Fires when a row is double clicked
5802          * @param {Roo.bootstrap.Table} this
5803          * @param {Roo.Element} el
5804          * @param {Number} rowIndex
5805          * @param {Roo.EventObject} e
5806          */
5807         "rowdblclick" : true,
5808         /**
5809          * @event mouseover
5810          * Fires when a mouseover occur
5811          * @param {Roo.bootstrap.Table} this
5812          * @param {Roo.Element} el
5813          * @param {Number} rowIndex
5814          * @param {Number} columnIndex
5815          * @param {Roo.EventObject} e
5816          */
5817         "mouseover" : true,
5818         /**
5819          * @event mouseout
5820          * Fires when a mouseout occur
5821          * @param {Roo.bootstrap.Table} this
5822          * @param {Roo.Element} el
5823          * @param {Number} rowIndex
5824          * @param {Number} columnIndex
5825          * @param {Roo.EventObject} e
5826          */
5827         "mouseout" : true,
5828         /**
5829          * @event rowclass
5830          * Fires when a row is rendered, so you can change add a style to it.
5831          * @param {Roo.bootstrap.Table} this
5832          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5833          */
5834         'rowclass' : true,
5835           /**
5836          * @event rowsrendered
5837          * Fires when all the  rows have been rendered
5838          * @param {Roo.bootstrap.Table} this
5839          */
5840         'rowsrendered' : true,
5841         /**
5842          * @event contextmenu
5843          * The raw contextmenu event for the entire grid.
5844          * @param {Roo.EventObject} e
5845          */
5846         "contextmenu" : true,
5847         /**
5848          * @event rowcontextmenu
5849          * Fires when a row is right clicked
5850          * @param {Roo.bootstrap.Table} this
5851          * @param {Number} rowIndex
5852          * @param {Roo.EventObject} e
5853          */
5854         "rowcontextmenu" : true,
5855         /**
5856          * @event cellcontextmenu
5857          * Fires when a cell is right clicked
5858          * @param {Roo.bootstrap.Table} this
5859          * @param {Number} rowIndex
5860          * @param {Number} cellIndex
5861          * @param {Roo.EventObject} e
5862          */
5863          "cellcontextmenu" : true,
5864          /**
5865          * @event headercontextmenu
5866          * Fires when a header is right clicked
5867          * @param {Roo.bootstrap.Table} this
5868          * @param {Number} columnIndex
5869          * @param {Roo.EventObject} e
5870          */
5871         "headercontextmenu" : true
5872     });
5873 };
5874
5875 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5876     
5877     cls: false,
5878     align: false,
5879     bgcolor: false,
5880     border: false,
5881     cellpadding: false,
5882     cellspacing: false,
5883     frame: false,
5884     rules: false,
5885     sortable: false,
5886     summary: false,
5887     width: false,
5888     striped : false,
5889     scrollBody : false,
5890     bordered: false,
5891     hover:  false,
5892     condensed : false,
5893     responsive : false,
5894     sm : false,
5895     cm : false,
5896     store : false,
5897     loadMask : false,
5898     footerShow : true,
5899     headerShow : true,
5900   
5901     rowSelection : false,
5902     cellSelection : false,
5903     layout : false,
5904     
5905     // Roo.Element - the tbody
5906     mainBody: false,
5907     // Roo.Element - thead element
5908     mainHead: false,
5909     
5910     container: false, // used by gridpanel...
5911     
5912     getAutoCreate : function()
5913     {
5914         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5915         
5916         cfg = {
5917             tag: 'table',
5918             cls : 'table',
5919             cn : []
5920         };
5921         if (this.scrollBody) {
5922             cfg.cls += ' table-body-fixed';
5923         }    
5924         if (this.striped) {
5925             cfg.cls += ' table-striped';
5926         }
5927         
5928         if (this.hover) {
5929             cfg.cls += ' table-hover';
5930         }
5931         if (this.bordered) {
5932             cfg.cls += ' table-bordered';
5933         }
5934         if (this.condensed) {
5935             cfg.cls += ' table-condensed';
5936         }
5937         if (this.responsive) {
5938             cfg.cls += ' table-responsive';
5939         }
5940         
5941         if (this.cls) {
5942             cfg.cls+=  ' ' +this.cls;
5943         }
5944         
5945         // this lot should be simplifed...
5946         
5947         if (this.align) {
5948             cfg.align=this.align;
5949         }
5950         if (this.bgcolor) {
5951             cfg.bgcolor=this.bgcolor;
5952         }
5953         if (this.border) {
5954             cfg.border=this.border;
5955         }
5956         if (this.cellpadding) {
5957             cfg.cellpadding=this.cellpadding;
5958         }
5959         if (this.cellspacing) {
5960             cfg.cellspacing=this.cellspacing;
5961         }
5962         if (this.frame) {
5963             cfg.frame=this.frame;
5964         }
5965         if (this.rules) {
5966             cfg.rules=this.rules;
5967         }
5968         if (this.sortable) {
5969             cfg.sortable=this.sortable;
5970         }
5971         if (this.summary) {
5972             cfg.summary=this.summary;
5973         }
5974         if (this.width) {
5975             cfg.width=this.width;
5976         }
5977         if (this.layout) {
5978             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5979         }
5980         
5981         if(this.store || this.cm){
5982             if(this.headerShow){
5983                 cfg.cn.push(this.renderHeader());
5984             }
5985             
5986             cfg.cn.push(this.renderBody());
5987             
5988             if(this.footerShow){
5989                 cfg.cn.push(this.renderFooter());
5990             }
5991             // where does this come from?
5992             //cfg.cls+=  ' TableGrid';
5993         }
5994         
5995         return { cn : [ cfg ] };
5996     },
5997     
5998     initEvents : function()
5999     {   
6000         if(!this.store || !this.cm){
6001             return;
6002         }
6003         if (this.selModel) {
6004             this.selModel.initEvents();
6005         }
6006         
6007         
6008         //Roo.log('initEvents with ds!!!!');
6009         
6010         this.mainBody = this.el.select('tbody', true).first();
6011         this.mainHead = this.el.select('thead', true).first();
6012         
6013         
6014         
6015         
6016         var _this = this;
6017         
6018         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6019             e.on('click', _this.sort, _this);
6020         });
6021         
6022         this.mainBody.on("click", this.onClick, this);
6023         this.mainBody.on("dblclick", this.onDblClick, this);
6024         
6025         // why is this done????? = it breaks dialogs??
6026         //this.parent().el.setStyle('position', 'relative');
6027         
6028         
6029         if (this.footer) {
6030             this.footer.parentId = this.id;
6031             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
6032         } 
6033         
6034         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6035         
6036         this.store.on('load', this.onLoad, this);
6037         this.store.on('beforeload', this.onBeforeLoad, this);
6038         this.store.on('update', this.onUpdate, this);
6039         this.store.on('add', this.onAdd, this);
6040         this.store.on("clear", this.clear, this);
6041         
6042         this.el.on("contextmenu", this.onContextMenu, this);
6043         
6044         this.mainBody.on('scroll', this.onBodyScroll, this);
6045         
6046         
6047     },
6048     
6049     onContextMenu : function(e, t)
6050     {
6051         this.processEvent("contextmenu", e);
6052     },
6053     
6054     processEvent : function(name, e)
6055     {
6056         if (name != 'touchstart' ) {
6057             this.fireEvent(name, e);    
6058         }
6059         
6060         var t = e.getTarget();
6061         
6062         var cell = Roo.get(t);
6063         
6064         if(!cell){
6065             return;
6066         }
6067         
6068         if(cell.findParent('tfoot', false, true)){
6069             return;
6070         }
6071         
6072         if(cell.findParent('thead', false, true)){
6073             
6074             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6075                 cell = Roo.get(t).findParent('th', false, true);
6076                 if (!cell) {
6077                     Roo.log("failed to find th in thead?");
6078                     Roo.log(e.getTarget());
6079                     return;
6080                 }
6081             }
6082             
6083             var cellIndex = cell.dom.cellIndex;
6084             
6085             var ename = name == 'touchstart' ? 'click' : name;
6086             this.fireEvent("header" + ename, this, cellIndex, e);
6087             
6088             return;
6089         }
6090         
6091         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6092             cell = Roo.get(t).findParent('td', false, true);
6093             if (!cell) {
6094                 Roo.log("failed to find th in tbody?");
6095                 Roo.log(e.getTarget());
6096                 return;
6097             }
6098         }
6099         
6100         var row = cell.findParent('tr', false, true);
6101         var cellIndex = cell.dom.cellIndex;
6102         var rowIndex = row.dom.rowIndex - 1;
6103         
6104         if(row !== false){
6105             
6106             this.fireEvent("row" + name, this, rowIndex, e);
6107             
6108             if(cell !== false){
6109             
6110                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6111             }
6112         }
6113         
6114     },
6115     
6116     onMouseover : function(e, el)
6117     {
6118         var cell = Roo.get(el);
6119         
6120         if(!cell){
6121             return;
6122         }
6123         
6124         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6125             cell = cell.findParent('td', false, true);
6126         }
6127         
6128         var row = cell.findParent('tr', false, true);
6129         var cellIndex = cell.dom.cellIndex;
6130         var rowIndex = row.dom.rowIndex - 1; // start from 0
6131         
6132         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6133         
6134     },
6135     
6136     onMouseout : function(e, el)
6137     {
6138         var cell = Roo.get(el);
6139         
6140         if(!cell){
6141             return;
6142         }
6143         
6144         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6145             cell = cell.findParent('td', false, true);
6146         }
6147         
6148         var row = cell.findParent('tr', false, true);
6149         var cellIndex = cell.dom.cellIndex;
6150         var rowIndex = row.dom.rowIndex - 1; // start from 0
6151         
6152         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6153         
6154     },
6155     
6156     onClick : function(e, el)
6157     {
6158         var cell = Roo.get(el);
6159         
6160         if(!cell || (!this.cellSelection && !this.rowSelection)){
6161             return;
6162         }
6163         
6164         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6165             cell = cell.findParent('td', false, true);
6166         }
6167         
6168         if(!cell || typeof(cell) == 'undefined'){
6169             return;
6170         }
6171         
6172         var row = cell.findParent('tr', false, true);
6173         
6174         if(!row || typeof(row) == 'undefined'){
6175             return;
6176         }
6177         
6178         var cellIndex = cell.dom.cellIndex;
6179         var rowIndex = this.getRowIndex(row);
6180         
6181         // why??? - should these not be based on SelectionModel?
6182         if(this.cellSelection){
6183             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6184         }
6185         
6186         if(this.rowSelection){
6187             this.fireEvent('rowclick', this, row, rowIndex, e);
6188         }
6189         
6190         
6191     },
6192         
6193     onDblClick : function(e,el)
6194     {
6195         var cell = Roo.get(el);
6196         
6197         if(!cell || (!this.cellSelection && !this.rowSelection)){
6198             return;
6199         }
6200         
6201         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6202             cell = cell.findParent('td', false, true);
6203         }
6204         
6205         if(!cell || typeof(cell) == 'undefined'){
6206             return;
6207         }
6208         
6209         var row = cell.findParent('tr', false, true);
6210         
6211         if(!row || typeof(row) == 'undefined'){
6212             return;
6213         }
6214         
6215         var cellIndex = cell.dom.cellIndex;
6216         var rowIndex = this.getRowIndex(row);
6217         
6218         if(this.cellSelection){
6219             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6220         }
6221         
6222         if(this.rowSelection){
6223             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6224         }
6225     },
6226     
6227     sort : function(e,el)
6228     {
6229         var col = Roo.get(el);
6230         
6231         if(!col.hasClass('sortable')){
6232             return;
6233         }
6234         
6235         var sort = col.attr('sort');
6236         var dir = 'ASC';
6237         
6238         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6239             dir = 'DESC';
6240         }
6241         
6242         this.store.sortInfo = {field : sort, direction : dir};
6243         
6244         if (this.footer) {
6245             Roo.log("calling footer first");
6246             this.footer.onClick('first');
6247         } else {
6248         
6249             this.store.load({ params : { start : 0 } });
6250         }
6251     },
6252     
6253     renderHeader : function()
6254     {
6255         var header = {
6256             tag: 'thead',
6257             cn : []
6258         };
6259         
6260         var cm = this.cm;
6261         this.totalWidth = 0;
6262         
6263         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6264             
6265             var config = cm.config[i];
6266             
6267             var c = {
6268                 tag: 'th',
6269                 style : '',
6270                 html: cm.getColumnHeader(i)
6271             };
6272             
6273             var hh = '';
6274             
6275             if(typeof(config.sortable) != 'undefined' && config.sortable){
6276                 c.cls = 'sortable';
6277                 c.html = '<i class="glyphicon"></i>' + c.html;
6278             }
6279             
6280             if(typeof(config.lgHeader) != 'undefined'){
6281                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6282             }
6283             
6284             if(typeof(config.mdHeader) != 'undefined'){
6285                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6286             }
6287             
6288             if(typeof(config.smHeader) != 'undefined'){
6289                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6290             }
6291             
6292             if(typeof(config.xsHeader) != 'undefined'){
6293                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6294             }
6295             
6296             if(hh.length){
6297                 c.html = hh;
6298             }
6299             
6300             if(typeof(config.tooltip) != 'undefined'){
6301                 c.tooltip = config.tooltip;
6302             }
6303             
6304             if(typeof(config.colspan) != 'undefined'){
6305                 c.colspan = config.colspan;
6306             }
6307             
6308             if(typeof(config.hidden) != 'undefined' && config.hidden){
6309                 c.style += ' display:none;';
6310             }
6311             
6312             if(typeof(config.dataIndex) != 'undefined'){
6313                 c.sort = config.dataIndex;
6314             }
6315             
6316            
6317             
6318             if(typeof(config.align) != 'undefined' && config.align.length){
6319                 c.style += ' text-align:' + config.align + ';';
6320             }
6321             
6322             if(typeof(config.width) != 'undefined'){
6323                 c.style += ' width:' + config.width + 'px;';
6324                 this.totalWidth += config.width;
6325             } else {
6326                 this.totalWidth += 100; // assume minimum of 100 per column?
6327             }
6328             
6329             if(typeof(config.cls) != 'undefined'){
6330                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6331             }
6332             
6333             ['xs','sm','md','lg'].map(function(size){
6334                 
6335                 if(typeof(config[size]) == 'undefined'){
6336                     return;
6337                 }
6338                 
6339                 if (!config[size]) { // 0 = hidden
6340                     c.cls += ' hidden-' + size;
6341                     return;
6342                 }
6343                 
6344                 c.cls += ' col-' + size + '-' + config[size];
6345
6346             });
6347             
6348             header.cn.push(c)
6349         }
6350         
6351         return header;
6352     },
6353     
6354     renderBody : function()
6355     {
6356         var body = {
6357             tag: 'tbody',
6358             cn : [
6359                 {
6360                     tag: 'tr',
6361                     cn : [
6362                         {
6363                             tag : 'td',
6364                             colspan :  this.cm.getColumnCount()
6365                         }
6366                     ]
6367                 }
6368             ]
6369         };
6370         
6371         return body;
6372     },
6373     
6374     renderFooter : function()
6375     {
6376         var footer = {
6377             tag: 'tfoot',
6378             cn : [
6379                 {
6380                     tag: 'tr',
6381                     cn : [
6382                         {
6383                             tag : 'td',
6384                             colspan :  this.cm.getColumnCount()
6385                         }
6386                     ]
6387                 }
6388             ]
6389         };
6390         
6391         return footer;
6392     },
6393     
6394     
6395     
6396     onLoad : function()
6397     {
6398 //        Roo.log('ds onload');
6399         this.clear();
6400         
6401         var _this = this;
6402         var cm = this.cm;
6403         var ds = this.store;
6404         
6405         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6406             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6407             if (_this.store.sortInfo) {
6408                     
6409                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6410                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6411                 }
6412                 
6413                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6414                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6415                 }
6416             }
6417         });
6418         
6419         var tbody =  this.mainBody;
6420               
6421         if(ds.getCount() > 0){
6422             ds.data.each(function(d,rowIndex){
6423                 var row =  this.renderRow(cm, ds, rowIndex);
6424                 
6425                 tbody.createChild(row);
6426                 
6427                 var _this = this;
6428                 
6429                 if(row.cellObjects.length){
6430                     Roo.each(row.cellObjects, function(r){
6431                         _this.renderCellObject(r);
6432                     })
6433                 }
6434                 
6435             }, this);
6436         }
6437         
6438         Roo.each(this.el.select('tbody td', true).elements, function(e){
6439             e.on('mouseover', _this.onMouseover, _this);
6440         });
6441         
6442         Roo.each(this.el.select('tbody td', true).elements, function(e){
6443             e.on('mouseout', _this.onMouseout, _this);
6444         });
6445         this.fireEvent('rowsrendered', this);
6446         //if(this.loadMask){
6447         //    this.maskEl.hide();
6448         //}
6449         
6450         this.autoSize();
6451     },
6452     
6453     
6454     onUpdate : function(ds,record)
6455     {
6456         this.refreshRow(record);
6457         this.autoSize();
6458     },
6459     
6460     onRemove : function(ds, record, index, isUpdate){
6461         if(isUpdate !== true){
6462             this.fireEvent("beforerowremoved", this, index, record);
6463         }
6464         var bt = this.mainBody.dom;
6465         
6466         var rows = this.el.select('tbody > tr', true).elements;
6467         
6468         if(typeof(rows[index]) != 'undefined'){
6469             bt.removeChild(rows[index].dom);
6470         }
6471         
6472 //        if(bt.rows[index]){
6473 //            bt.removeChild(bt.rows[index]);
6474 //        }
6475         
6476         if(isUpdate !== true){
6477             //this.stripeRows(index);
6478             //this.syncRowHeights(index, index);
6479             //this.layout();
6480             this.fireEvent("rowremoved", this, index, record);
6481         }
6482     },
6483     
6484     onAdd : function(ds, records, rowIndex)
6485     {
6486         //Roo.log('on Add called');
6487         // - note this does not handle multiple adding very well..
6488         var bt = this.mainBody.dom;
6489         for (var i =0 ; i < records.length;i++) {
6490             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6491             //Roo.log(records[i]);
6492             //Roo.log(this.store.getAt(rowIndex+i));
6493             this.insertRow(this.store, rowIndex + i, false);
6494             return;
6495         }
6496         
6497     },
6498     
6499     
6500     refreshRow : function(record){
6501         var ds = this.store, index;
6502         if(typeof record == 'number'){
6503             index = record;
6504             record = ds.getAt(index);
6505         }else{
6506             index = ds.indexOf(record);
6507         }
6508         this.insertRow(ds, index, true);
6509         this.autoSize();
6510         this.onRemove(ds, record, index+1, true);
6511         this.autoSize();
6512         //this.syncRowHeights(index, index);
6513         //this.layout();
6514         this.fireEvent("rowupdated", this, index, record);
6515     },
6516     
6517     insertRow : function(dm, rowIndex, isUpdate){
6518         
6519         if(!isUpdate){
6520             this.fireEvent("beforerowsinserted", this, rowIndex);
6521         }
6522             //var s = this.getScrollState();
6523         var row = this.renderRow(this.cm, this.store, rowIndex);
6524         // insert before rowIndex..
6525         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6526         
6527         var _this = this;
6528                 
6529         if(row.cellObjects.length){
6530             Roo.each(row.cellObjects, function(r){
6531                 _this.renderCellObject(r);
6532             })
6533         }
6534             
6535         if(!isUpdate){
6536             this.fireEvent("rowsinserted", this, rowIndex);
6537             //this.syncRowHeights(firstRow, lastRow);
6538             //this.stripeRows(firstRow);
6539             //this.layout();
6540         }
6541         
6542     },
6543     
6544     
6545     getRowDom : function(rowIndex)
6546     {
6547         var rows = this.el.select('tbody > tr', true).elements;
6548         
6549         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6550         
6551     },
6552     // returns the object tree for a tr..
6553   
6554     
6555     renderRow : function(cm, ds, rowIndex) 
6556     {
6557         
6558         var d = ds.getAt(rowIndex);
6559         
6560         var row = {
6561             tag : 'tr',
6562             cn : []
6563         };
6564             
6565         var cellObjects = [];
6566         
6567         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6568             var config = cm.config[i];
6569             
6570             var renderer = cm.getRenderer(i);
6571             var value = '';
6572             var id = false;
6573             
6574             if(typeof(renderer) !== 'undefined'){
6575                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6576             }
6577             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6578             // and are rendered into the cells after the row is rendered - using the id for the element.
6579             
6580             if(typeof(value) === 'object'){
6581                 id = Roo.id();
6582                 cellObjects.push({
6583                     container : id,
6584                     cfg : value 
6585                 })
6586             }
6587             
6588             var rowcfg = {
6589                 record: d,
6590                 rowIndex : rowIndex,
6591                 colIndex : i,
6592                 rowClass : ''
6593             };
6594
6595             this.fireEvent('rowclass', this, rowcfg);
6596             
6597             var td = {
6598                 tag: 'td',
6599                 cls : rowcfg.rowClass,
6600                 style: '',
6601                 html: (typeof(value) === 'object') ? '' : value
6602             };
6603             
6604             if (id) {
6605                 td.id = id;
6606             }
6607             
6608             if(typeof(config.colspan) != 'undefined'){
6609                 td.colspan = config.colspan;
6610             }
6611             
6612             if(typeof(config.hidden) != 'undefined' && config.hidden){
6613                 td.style += ' display:none;';
6614             }
6615             
6616             if(typeof(config.align) != 'undefined' && config.align.length){
6617                 td.style += ' text-align:' + config.align + ';';
6618             }
6619             
6620             if(typeof(config.width) != 'undefined'){
6621                 td.style += ' width:' +  config.width + 'px;';
6622             }
6623             
6624             if(typeof(config.cursor) != 'undefined'){
6625                 td.style += ' cursor:' +  config.cursor + ';';
6626             }
6627             
6628             if(typeof(config.cls) != 'undefined'){
6629                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6630             }
6631             
6632             ['xs','sm','md','lg'].map(function(size){
6633                 
6634                 if(typeof(config[size]) == 'undefined'){
6635                     return;
6636                 }
6637                 
6638                 if (!config[size]) { // 0 = hidden
6639                     td.cls += ' hidden-' + size;
6640                     return;
6641                 }
6642                 
6643                 td.cls += ' col-' + size + '-' + config[size];
6644
6645             });
6646              
6647             row.cn.push(td);
6648            
6649         }
6650         
6651         row.cellObjects = cellObjects;
6652         
6653         return row;
6654           
6655     },
6656     
6657     
6658     
6659     onBeforeLoad : function()
6660     {
6661         //Roo.log('ds onBeforeLoad');
6662         
6663         //this.clear();
6664         
6665         //if(this.loadMask){
6666         //    this.maskEl.show();
6667         //}
6668     },
6669      /**
6670      * Remove all rows
6671      */
6672     clear : function()
6673     {
6674         this.el.select('tbody', true).first().dom.innerHTML = '';
6675     },
6676     /**
6677      * Show or hide a row.
6678      * @param {Number} rowIndex to show or hide
6679      * @param {Boolean} state hide
6680      */
6681     setRowVisibility : function(rowIndex, state)
6682     {
6683         var bt = this.mainBody.dom;
6684         
6685         var rows = this.el.select('tbody > tr', true).elements;
6686         
6687         if(typeof(rows[rowIndex]) == 'undefined'){
6688             return;
6689         }
6690         rows[rowIndex].dom.style.display = state ? '' : 'none';
6691     },
6692     
6693     
6694     getSelectionModel : function(){
6695         if(!this.selModel){
6696             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6697         }
6698         return this.selModel;
6699     },
6700     /*
6701      * Render the Roo.bootstrap object from renderder
6702      */
6703     renderCellObject : function(r)
6704     {
6705         var _this = this;
6706         
6707         var t = r.cfg.render(r.container);
6708         
6709         if(r.cfg.cn){
6710             Roo.each(r.cfg.cn, function(c){
6711                 var child = {
6712                     container: t.getChildContainer(),
6713                     cfg: c
6714                 };
6715                 _this.renderCellObject(child);
6716             })
6717         }
6718     },
6719     
6720     getRowIndex : function(row)
6721     {
6722         var rowIndex = -1;
6723         
6724         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6725             if(el != row){
6726                 return;
6727             }
6728             
6729             rowIndex = index;
6730         });
6731         
6732         return rowIndex;
6733     },
6734      /**
6735      * Returns the grid's underlying element = used by panel.Grid
6736      * @return {Element} The element
6737      */
6738     getGridEl : function(){
6739         return this.el;
6740     },
6741      /**
6742      * Forces a resize - used by panel.Grid
6743      * @return {Element} The element
6744      */
6745     autoSize : function()
6746     {
6747         //var ctr = Roo.get(this.container.dom.parentElement);
6748         var ctr = Roo.get(this.el.dom);
6749         
6750         var thd = this.getGridEl().select('thead',true).first();
6751         var tbd = this.getGridEl().select('tbody', true).first();
6752         var tfd = this.getGridEl().select('tfoot', true).first();
6753         
6754         var cw = ctr.getWidth();
6755         
6756         if (tbd) {
6757             
6758             tbd.setSize(ctr.getWidth(),
6759                         ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6760             );
6761             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6762             cw -= barsize;
6763         }
6764         cw = Math.max(cw, this.totalWidth);
6765         this.getGridEl().select('tr',true).setWidth(cw);
6766         // resize 'expandable coloumn?
6767         
6768         return; // we doe not have a view in this design..
6769         
6770     },
6771     onBodyScroll: function()
6772     {
6773         
6774         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6775         this.mainHead.setStyle({
6776                     'position' : 'relative',
6777                     'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6778         });
6779         
6780         
6781     }
6782 });
6783
6784  
6785
6786  /*
6787  * - LGPL
6788  *
6789  * table cell
6790  * 
6791  */
6792
6793 /**
6794  * @class Roo.bootstrap.TableCell
6795  * @extends Roo.bootstrap.Component
6796  * Bootstrap TableCell class
6797  * @cfg {String} html cell contain text
6798  * @cfg {String} cls cell class
6799  * @cfg {String} tag cell tag (td|th) default td
6800  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6801  * @cfg {String} align Aligns the content in a cell
6802  * @cfg {String} axis Categorizes cells
6803  * @cfg {String} bgcolor Specifies the background color of a cell
6804  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6805  * @cfg {Number} colspan Specifies the number of columns a cell should span
6806  * @cfg {String} headers Specifies one or more header cells a cell is related to
6807  * @cfg {Number} height Sets the height of a cell
6808  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6809  * @cfg {Number} rowspan Sets the number of rows a cell should span
6810  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6811  * @cfg {String} valign Vertical aligns the content in a cell
6812  * @cfg {Number} width Specifies the width of a cell
6813  * 
6814  * @constructor
6815  * Create a new TableCell
6816  * @param {Object} config The config object
6817  */
6818
6819 Roo.bootstrap.TableCell = function(config){
6820     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6821 };
6822
6823 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6824     
6825     html: false,
6826     cls: false,
6827     tag: false,
6828     abbr: false,
6829     align: false,
6830     axis: false,
6831     bgcolor: false,
6832     charoff: false,
6833     colspan: false,
6834     headers: false,
6835     height: false,
6836     nowrap: false,
6837     rowspan: false,
6838     scope: false,
6839     valign: false,
6840     width: false,
6841     
6842     
6843     getAutoCreate : function(){
6844         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6845         
6846         cfg = {
6847             tag: 'td'
6848         };
6849         
6850         if(this.tag){
6851             cfg.tag = this.tag;
6852         }
6853         
6854         if (this.html) {
6855             cfg.html=this.html
6856         }
6857         if (this.cls) {
6858             cfg.cls=this.cls
6859         }
6860         if (this.abbr) {
6861             cfg.abbr=this.abbr
6862         }
6863         if (this.align) {
6864             cfg.align=this.align
6865         }
6866         if (this.axis) {
6867             cfg.axis=this.axis
6868         }
6869         if (this.bgcolor) {
6870             cfg.bgcolor=this.bgcolor
6871         }
6872         if (this.charoff) {
6873             cfg.charoff=this.charoff
6874         }
6875         if (this.colspan) {
6876             cfg.colspan=this.colspan
6877         }
6878         if (this.headers) {
6879             cfg.headers=this.headers
6880         }
6881         if (this.height) {
6882             cfg.height=this.height
6883         }
6884         if (this.nowrap) {
6885             cfg.nowrap=this.nowrap
6886         }
6887         if (this.rowspan) {
6888             cfg.rowspan=this.rowspan
6889         }
6890         if (this.scope) {
6891             cfg.scope=this.scope
6892         }
6893         if (this.valign) {
6894             cfg.valign=this.valign
6895         }
6896         if (this.width) {
6897             cfg.width=this.width
6898         }
6899         
6900         
6901         return cfg;
6902     }
6903    
6904 });
6905
6906  
6907
6908  /*
6909  * - LGPL
6910  *
6911  * table row
6912  * 
6913  */
6914
6915 /**
6916  * @class Roo.bootstrap.TableRow
6917  * @extends Roo.bootstrap.Component
6918  * Bootstrap TableRow class
6919  * @cfg {String} cls row class
6920  * @cfg {String} align Aligns the content in a table row
6921  * @cfg {String} bgcolor Specifies a background color for a table row
6922  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6923  * @cfg {String} valign Vertical aligns the content in a table row
6924  * 
6925  * @constructor
6926  * Create a new TableRow
6927  * @param {Object} config The config object
6928  */
6929
6930 Roo.bootstrap.TableRow = function(config){
6931     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6932 };
6933
6934 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6935     
6936     cls: false,
6937     align: false,
6938     bgcolor: false,
6939     charoff: false,
6940     valign: false,
6941     
6942     getAutoCreate : function(){
6943         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6944         
6945         cfg = {
6946             tag: 'tr'
6947         };
6948             
6949         if(this.cls){
6950             cfg.cls = this.cls;
6951         }
6952         if(this.align){
6953             cfg.align = this.align;
6954         }
6955         if(this.bgcolor){
6956             cfg.bgcolor = this.bgcolor;
6957         }
6958         if(this.charoff){
6959             cfg.charoff = this.charoff;
6960         }
6961         if(this.valign){
6962             cfg.valign = this.valign;
6963         }
6964         
6965         return cfg;
6966     }
6967    
6968 });
6969
6970  
6971
6972  /*
6973  * - LGPL
6974  *
6975  * table body
6976  * 
6977  */
6978
6979 /**
6980  * @class Roo.bootstrap.TableBody
6981  * @extends Roo.bootstrap.Component
6982  * Bootstrap TableBody class
6983  * @cfg {String} cls element class
6984  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6985  * @cfg {String} align Aligns the content inside the element
6986  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6987  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6988  * 
6989  * @constructor
6990  * Create a new TableBody
6991  * @param {Object} config The config object
6992  */
6993
6994 Roo.bootstrap.TableBody = function(config){
6995     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6996 };
6997
6998 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6999     
7000     cls: false,
7001     tag: false,
7002     align: false,
7003     charoff: false,
7004     valign: false,
7005     
7006     getAutoCreate : function(){
7007         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7008         
7009         cfg = {
7010             tag: 'tbody'
7011         };
7012             
7013         if (this.cls) {
7014             cfg.cls=this.cls
7015         }
7016         if(this.tag){
7017             cfg.tag = this.tag;
7018         }
7019         
7020         if(this.align){
7021             cfg.align = this.align;
7022         }
7023         if(this.charoff){
7024             cfg.charoff = this.charoff;
7025         }
7026         if(this.valign){
7027             cfg.valign = this.valign;
7028         }
7029         
7030         return cfg;
7031     }
7032     
7033     
7034 //    initEvents : function()
7035 //    {
7036 //        
7037 //        if(!this.store){
7038 //            return;
7039 //        }
7040 //        
7041 //        this.store = Roo.factory(this.store, Roo.data);
7042 //        this.store.on('load', this.onLoad, this);
7043 //        
7044 //        this.store.load();
7045 //        
7046 //    },
7047 //    
7048 //    onLoad: function () 
7049 //    {   
7050 //        this.fireEvent('load', this);
7051 //    }
7052 //    
7053 //   
7054 });
7055
7056  
7057
7058  /*
7059  * Based on:
7060  * Ext JS Library 1.1.1
7061  * Copyright(c) 2006-2007, Ext JS, LLC.
7062  *
7063  * Originally Released Under LGPL - original licence link has changed is not relivant.
7064  *
7065  * Fork - LGPL
7066  * <script type="text/javascript">
7067  */
7068
7069 // as we use this in bootstrap.
7070 Roo.namespace('Roo.form');
7071  /**
7072  * @class Roo.form.Action
7073  * Internal Class used to handle form actions
7074  * @constructor
7075  * @param {Roo.form.BasicForm} el The form element or its id
7076  * @param {Object} config Configuration options
7077  */
7078
7079  
7080  
7081 // define the action interface
7082 Roo.form.Action = function(form, options){
7083     this.form = form;
7084     this.options = options || {};
7085 };
7086 /**
7087  * Client Validation Failed
7088  * @const 
7089  */
7090 Roo.form.Action.CLIENT_INVALID = 'client';
7091 /**
7092  * Server Validation Failed
7093  * @const 
7094  */
7095 Roo.form.Action.SERVER_INVALID = 'server';
7096  /**
7097  * Connect to Server Failed
7098  * @const 
7099  */
7100 Roo.form.Action.CONNECT_FAILURE = 'connect';
7101 /**
7102  * Reading Data from Server Failed
7103  * @const 
7104  */
7105 Roo.form.Action.LOAD_FAILURE = 'load';
7106
7107 Roo.form.Action.prototype = {
7108     type : 'default',
7109     failureType : undefined,
7110     response : undefined,
7111     result : undefined,
7112
7113     // interface method
7114     run : function(options){
7115
7116     },
7117
7118     // interface method
7119     success : function(response){
7120
7121     },
7122
7123     // interface method
7124     handleResponse : function(response){
7125
7126     },
7127
7128     // default connection failure
7129     failure : function(response){
7130         
7131         this.response = response;
7132         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7133         this.form.afterAction(this, false);
7134     },
7135
7136     processResponse : function(response){
7137         this.response = response;
7138         if(!response.responseText){
7139             return true;
7140         }
7141         this.result = this.handleResponse(response);
7142         return this.result;
7143     },
7144
7145     // utility functions used internally
7146     getUrl : function(appendParams){
7147         var url = this.options.url || this.form.url || this.form.el.dom.action;
7148         if(appendParams){
7149             var p = this.getParams();
7150             if(p){
7151                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7152             }
7153         }
7154         return url;
7155     },
7156
7157     getMethod : function(){
7158         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7159     },
7160
7161     getParams : function(){
7162         var bp = this.form.baseParams;
7163         var p = this.options.params;
7164         if(p){
7165             if(typeof p == "object"){
7166                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7167             }else if(typeof p == 'string' && bp){
7168                 p += '&' + Roo.urlEncode(bp);
7169             }
7170         }else if(bp){
7171             p = Roo.urlEncode(bp);
7172         }
7173         return p;
7174     },
7175
7176     createCallback : function(){
7177         return {
7178             success: this.success,
7179             failure: this.failure,
7180             scope: this,
7181             timeout: (this.form.timeout*1000),
7182             upload: this.form.fileUpload ? this.success : undefined
7183         };
7184     }
7185 };
7186
7187 Roo.form.Action.Submit = function(form, options){
7188     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7189 };
7190
7191 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7192     type : 'submit',
7193
7194     haveProgress : false,
7195     uploadComplete : false,
7196     
7197     // uploadProgress indicator.
7198     uploadProgress : function()
7199     {
7200         if (!this.form.progressUrl) {
7201             return;
7202         }
7203         
7204         if (!this.haveProgress) {
7205             Roo.MessageBox.progress("Uploading", "Uploading");
7206         }
7207         if (this.uploadComplete) {
7208            Roo.MessageBox.hide();
7209            return;
7210         }
7211         
7212         this.haveProgress = true;
7213    
7214         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7215         
7216         var c = new Roo.data.Connection();
7217         c.request({
7218             url : this.form.progressUrl,
7219             params: {
7220                 id : uid
7221             },
7222             method: 'GET',
7223             success : function(req){
7224                //console.log(data);
7225                 var rdata = false;
7226                 var edata;
7227                 try  {
7228                    rdata = Roo.decode(req.responseText)
7229                 } catch (e) {
7230                     Roo.log("Invalid data from server..");
7231                     Roo.log(edata);
7232                     return;
7233                 }
7234                 if (!rdata || !rdata.success) {
7235                     Roo.log(rdata);
7236                     Roo.MessageBox.alert(Roo.encode(rdata));
7237                     return;
7238                 }
7239                 var data = rdata.data;
7240                 
7241                 if (this.uploadComplete) {
7242                    Roo.MessageBox.hide();
7243                    return;
7244                 }
7245                    
7246                 if (data){
7247                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7248                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7249                     );
7250                 }
7251                 this.uploadProgress.defer(2000,this);
7252             },
7253        
7254             failure: function(data) {
7255                 Roo.log('progress url failed ');
7256                 Roo.log(data);
7257             },
7258             scope : this
7259         });
7260            
7261     },
7262     
7263     
7264     run : function()
7265     {
7266         // run get Values on the form, so it syncs any secondary forms.
7267         this.form.getValues();
7268         
7269         var o = this.options;
7270         var method = this.getMethod();
7271         var isPost = method == 'POST';
7272         if(o.clientValidation === false || this.form.isValid()){
7273             
7274             if (this.form.progressUrl) {
7275                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7276                     (new Date() * 1) + '' + Math.random());
7277                     
7278             } 
7279             
7280             
7281             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7282                 form:this.form.el.dom,
7283                 url:this.getUrl(!isPost),
7284                 method: method,
7285                 params:isPost ? this.getParams() : null,
7286                 isUpload: this.form.fileUpload
7287             }));
7288             
7289             this.uploadProgress();
7290
7291         }else if (o.clientValidation !== false){ // client validation failed
7292             this.failureType = Roo.form.Action.CLIENT_INVALID;
7293             this.form.afterAction(this, false);
7294         }
7295     },
7296
7297     success : function(response)
7298     {
7299         this.uploadComplete= true;
7300         if (this.haveProgress) {
7301             Roo.MessageBox.hide();
7302         }
7303         
7304         
7305         var result = this.processResponse(response);
7306         if(result === true || result.success){
7307             this.form.afterAction(this, true);
7308             return;
7309         }
7310         if(result.errors){
7311             this.form.markInvalid(result.errors);
7312             this.failureType = Roo.form.Action.SERVER_INVALID;
7313         }
7314         this.form.afterAction(this, false);
7315     },
7316     failure : function(response)
7317     {
7318         this.uploadComplete= true;
7319         if (this.haveProgress) {
7320             Roo.MessageBox.hide();
7321         }
7322         
7323         this.response = response;
7324         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7325         this.form.afterAction(this, false);
7326     },
7327     
7328     handleResponse : function(response){
7329         if(this.form.errorReader){
7330             var rs = this.form.errorReader.read(response);
7331             var errors = [];
7332             if(rs.records){
7333                 for(var i = 0, len = rs.records.length; i < len; i++) {
7334                     var r = rs.records[i];
7335                     errors[i] = r.data;
7336                 }
7337             }
7338             if(errors.length < 1){
7339                 errors = null;
7340             }
7341             return {
7342                 success : rs.success,
7343                 errors : errors
7344             };
7345         }
7346         var ret = false;
7347         try {
7348             ret = Roo.decode(response.responseText);
7349         } catch (e) {
7350             ret = {
7351                 success: false,
7352                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7353                 errors : []
7354             };
7355         }
7356         return ret;
7357         
7358     }
7359 });
7360
7361
7362 Roo.form.Action.Load = function(form, options){
7363     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7364     this.reader = this.form.reader;
7365 };
7366
7367 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7368     type : 'load',
7369
7370     run : function(){
7371         
7372         Roo.Ajax.request(Roo.apply(
7373                 this.createCallback(), {
7374                     method:this.getMethod(),
7375                     url:this.getUrl(false),
7376                     params:this.getParams()
7377         }));
7378     },
7379
7380     success : function(response){
7381         
7382         var result = this.processResponse(response);
7383         if(result === true || !result.success || !result.data){
7384             this.failureType = Roo.form.Action.LOAD_FAILURE;
7385             this.form.afterAction(this, false);
7386             return;
7387         }
7388         this.form.clearInvalid();
7389         this.form.setValues(result.data);
7390         this.form.afterAction(this, true);
7391     },
7392
7393     handleResponse : function(response){
7394         if(this.form.reader){
7395             var rs = this.form.reader.read(response);
7396             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7397             return {
7398                 success : rs.success,
7399                 data : data
7400             };
7401         }
7402         return Roo.decode(response.responseText);
7403     }
7404 });
7405
7406 Roo.form.Action.ACTION_TYPES = {
7407     'load' : Roo.form.Action.Load,
7408     'submit' : Roo.form.Action.Submit
7409 };/*
7410  * - LGPL
7411  *
7412  * form
7413  *
7414  */
7415
7416 /**
7417  * @class Roo.bootstrap.Form
7418  * @extends Roo.bootstrap.Component
7419  * Bootstrap Form class
7420  * @cfg {String} method  GET | POST (default POST)
7421  * @cfg {String} labelAlign top | left (default top)
7422  * @cfg {String} align left  | right - for navbars
7423  * @cfg {Boolean} loadMask load mask when submit (default true)
7424
7425  *
7426  * @constructor
7427  * Create a new Form
7428  * @param {Object} config The config object
7429  */
7430
7431
7432 Roo.bootstrap.Form = function(config){
7433     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7434     this.addEvents({
7435         /**
7436          * @event clientvalidation
7437          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7438          * @param {Form} this
7439          * @param {Boolean} valid true if the form has passed client-side validation
7440          */
7441         clientvalidation: true,
7442         /**
7443          * @event beforeaction
7444          * Fires before any action is performed. Return false to cancel the action.
7445          * @param {Form} this
7446          * @param {Action} action The action to be performed
7447          */
7448         beforeaction: true,
7449         /**
7450          * @event actionfailed
7451          * Fires when an action fails.
7452          * @param {Form} this
7453          * @param {Action} action The action that failed
7454          */
7455         actionfailed : true,
7456         /**
7457          * @event actioncomplete
7458          * Fires when an action is completed.
7459          * @param {Form} this
7460          * @param {Action} action The action that completed
7461          */
7462         actioncomplete : true
7463     });
7464
7465 };
7466
7467 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7468
7469      /**
7470      * @cfg {String} method
7471      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7472      */
7473     method : 'POST',
7474     /**
7475      * @cfg {String} url
7476      * The URL to use for form actions if one isn't supplied in the action options.
7477      */
7478     /**
7479      * @cfg {Boolean} fileUpload
7480      * Set to true if this form is a file upload.
7481      */
7482
7483     /**
7484      * @cfg {Object} baseParams
7485      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7486      */
7487
7488     /**
7489      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7490      */
7491     timeout: 30,
7492     /**
7493      * @cfg {Sting} align (left|right) for navbar forms
7494      */
7495     align : 'left',
7496
7497     // private
7498     activeAction : null,
7499
7500     /**
7501      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7502      * element by passing it or its id or mask the form itself by passing in true.
7503      * @type Mixed
7504      */
7505     waitMsgTarget : false,
7506
7507     loadMask : true,
7508
7509     getAutoCreate : function(){
7510
7511         var cfg = {
7512             tag: 'form',
7513             method : this.method || 'POST',
7514             id : this.id || Roo.id(),
7515             cls : ''
7516         };
7517         if (this.parent().xtype.match(/^Nav/)) {
7518             cfg.cls = 'navbar-form navbar-' + this.align;
7519
7520         }
7521
7522         if (this.labelAlign == 'left' ) {
7523             cfg.cls += ' form-horizontal';
7524         }
7525
7526
7527         return cfg;
7528     },
7529     initEvents : function()
7530     {
7531         this.el.on('submit', this.onSubmit, this);
7532         // this was added as random key presses on the form where triggering form submit.
7533         this.el.on('keypress', function(e) {
7534             if (e.getCharCode() != 13) {
7535                 return true;
7536             }
7537             // we might need to allow it for textareas.. and some other items.
7538             // check e.getTarget().
7539
7540             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7541                 return true;
7542             }
7543
7544             Roo.log("keypress blocked");
7545
7546             e.preventDefault();
7547             return false;
7548         });
7549
7550     },
7551     // private
7552     onSubmit : function(e){
7553         e.stopEvent();
7554     },
7555
7556      /**
7557      * Returns true if client-side validation on the form is successful.
7558      * @return Boolean
7559      */
7560     isValid : function(){
7561         var items = this.getItems();
7562         var valid = true;
7563         items.each(function(f){
7564            if(!f.validate()){
7565                valid = false;
7566
7567            }
7568         });
7569         return valid;
7570     },
7571     /**
7572      * Returns true if any fields in this form have changed since their original load.
7573      * @return Boolean
7574      */
7575     isDirty : function(){
7576         var dirty = false;
7577         var items = this.getItems();
7578         items.each(function(f){
7579            if(f.isDirty()){
7580                dirty = true;
7581                return false;
7582            }
7583            return true;
7584         });
7585         return dirty;
7586     },
7587      /**
7588      * Performs a predefined action (submit or load) or custom actions you define on this form.
7589      * @param {String} actionName The name of the action type
7590      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7591      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7592      * accept other config options):
7593      * <pre>
7594 Property          Type             Description
7595 ----------------  ---------------  ----------------------------------------------------------------------------------
7596 url               String           The url for the action (defaults to the form's url)
7597 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7598 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7599 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7600                                    validate the form on the client (defaults to false)
7601      * </pre>
7602      * @return {BasicForm} this
7603      */
7604     doAction : function(action, options){
7605         if(typeof action == 'string'){
7606             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7607         }
7608         if(this.fireEvent('beforeaction', this, action) !== false){
7609             this.beforeAction(action);
7610             action.run.defer(100, action);
7611         }
7612         return this;
7613     },
7614
7615     // private
7616     beforeAction : function(action){
7617         var o = action.options;
7618
7619         if(this.loadMask){
7620             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7621         }
7622         // not really supported yet.. ??
7623
7624         //if(this.waitMsgTarget === true){
7625         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7626         //}else if(this.waitMsgTarget){
7627         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7628         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7629         //}else {
7630         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7631        // }
7632
7633     },
7634
7635     // private
7636     afterAction : function(action, success){
7637         this.activeAction = null;
7638         var o = action.options;
7639
7640         //if(this.waitMsgTarget === true){
7641             this.el.unmask();
7642         //}else if(this.waitMsgTarget){
7643         //    this.waitMsgTarget.unmask();
7644         //}else{
7645         //    Roo.MessageBox.updateProgress(1);
7646         //    Roo.MessageBox.hide();
7647        // }
7648         //
7649         if(success){
7650             if(o.reset){
7651                 this.reset();
7652             }
7653             Roo.callback(o.success, o.scope, [this, action]);
7654             this.fireEvent('actioncomplete', this, action);
7655
7656         }else{
7657
7658             // failure condition..
7659             // we have a scenario where updates need confirming.
7660             // eg. if a locking scenario exists..
7661             // we look for { errors : { needs_confirm : true }} in the response.
7662             if (
7663                 (typeof(action.result) != 'undefined')  &&
7664                 (typeof(action.result.errors) != 'undefined')  &&
7665                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7666            ){
7667                 var _t = this;
7668                 Roo.log("not supported yet");
7669                  /*
7670
7671                 Roo.MessageBox.confirm(
7672                     "Change requires confirmation",
7673                     action.result.errorMsg,
7674                     function(r) {
7675                         if (r != 'yes') {
7676                             return;
7677                         }
7678                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7679                     }
7680
7681                 );
7682                 */
7683
7684
7685                 return;
7686             }
7687
7688             Roo.callback(o.failure, o.scope, [this, action]);
7689             // show an error message if no failed handler is set..
7690             if (!this.hasListener('actionfailed')) {
7691                 Roo.log("need to add dialog support");
7692                 /*
7693                 Roo.MessageBox.alert("Error",
7694                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7695                         action.result.errorMsg :
7696                         "Saving Failed, please check your entries or try again"
7697                 );
7698                 */
7699             }
7700
7701             this.fireEvent('actionfailed', this, action);
7702         }
7703
7704     },
7705     /**
7706      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7707      * @param {String} id The value to search for
7708      * @return Field
7709      */
7710     findField : function(id){
7711         var items = this.getItems();
7712         var field = items.get(id);
7713         if(!field){
7714              items.each(function(f){
7715                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7716                     field = f;
7717                     return false;
7718                 }
7719                 return true;
7720             });
7721         }
7722         return field || null;
7723     },
7724      /**
7725      * Mark fields in this form invalid in bulk.
7726      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7727      * @return {BasicForm} this
7728      */
7729     markInvalid : function(errors){
7730         if(errors instanceof Array){
7731             for(var i = 0, len = errors.length; i < len; i++){
7732                 var fieldError = errors[i];
7733                 var f = this.findField(fieldError.id);
7734                 if(f){
7735                     f.markInvalid(fieldError.msg);
7736                 }
7737             }
7738         }else{
7739             var field, id;
7740             for(id in errors){
7741                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7742                     field.markInvalid(errors[id]);
7743                 }
7744             }
7745         }
7746         //Roo.each(this.childForms || [], function (f) {
7747         //    f.markInvalid(errors);
7748         //});
7749
7750         return this;
7751     },
7752
7753     /**
7754      * Set values for fields in this form in bulk.
7755      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7756      * @return {BasicForm} this
7757      */
7758     setValues : function(values){
7759         if(values instanceof Array){ // array of objects
7760             for(var i = 0, len = values.length; i < len; i++){
7761                 var v = values[i];
7762                 var f = this.findField(v.id);
7763                 if(f){
7764                     f.setValue(v.value);
7765                     if(this.trackResetOnLoad){
7766                         f.originalValue = f.getValue();
7767                     }
7768                 }
7769             }
7770         }else{ // object hash
7771             var field, id;
7772             for(id in values){
7773                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7774
7775                     if (field.setFromData &&
7776                         field.valueField &&
7777                         field.displayField &&
7778                         // combos' with local stores can
7779                         // be queried via setValue()
7780                         // to set their value..
7781                         (field.store && !field.store.isLocal)
7782                         ) {
7783                         // it's a combo
7784                         var sd = { };
7785                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7786                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7787                         field.setFromData(sd);
7788
7789                     } else {
7790                         field.setValue(values[id]);
7791                     }
7792
7793
7794                     if(this.trackResetOnLoad){
7795                         field.originalValue = field.getValue();
7796                     }
7797                 }
7798             }
7799         }
7800
7801         //Roo.each(this.childForms || [], function (f) {
7802         //    f.setValues(values);
7803         //});
7804
7805         return this;
7806     },
7807
7808     /**
7809      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7810      * they are returned as an array.
7811      * @param {Boolean} asString
7812      * @return {Object}
7813      */
7814     getValues : function(asString){
7815         //if (this.childForms) {
7816             // copy values from the child forms
7817         //    Roo.each(this.childForms, function (f) {
7818         //        this.setValues(f.getValues());
7819         //    }, this);
7820         //}
7821
7822
7823
7824         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7825         if(asString === true){
7826             return fs;
7827         }
7828         return Roo.urlDecode(fs);
7829     },
7830
7831     /**
7832      * Returns the fields in this form as an object with key/value pairs.
7833      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7834      * @return {Object}
7835      */
7836     getFieldValues : function(with_hidden)
7837     {
7838         var items = this.getItems();
7839         var ret = {};
7840         items.each(function(f){
7841             if (!f.getName()) {
7842                 return;
7843             }
7844             var v = f.getValue();
7845             if (f.inputType =='radio') {
7846                 if (typeof(ret[f.getName()]) == 'undefined') {
7847                     ret[f.getName()] = ''; // empty..
7848                 }
7849
7850                 if (!f.el.dom.checked) {
7851                     return;
7852
7853                 }
7854                 v = f.el.dom.value;
7855
7856             }
7857
7858             // not sure if this supported any more..
7859             if ((typeof(v) == 'object') && f.getRawValue) {
7860                 v = f.getRawValue() ; // dates..
7861             }
7862             // combo boxes where name != hiddenName...
7863             if (f.name != f.getName()) {
7864                 ret[f.name] = f.getRawValue();
7865             }
7866             ret[f.getName()] = v;
7867         });
7868
7869         return ret;
7870     },
7871
7872     /**
7873      * Clears all invalid messages in this form.
7874      * @return {BasicForm} this
7875      */
7876     clearInvalid : function(){
7877         var items = this.getItems();
7878
7879         items.each(function(f){
7880            f.clearInvalid();
7881         });
7882
7883
7884
7885         return this;
7886     },
7887
7888     /**
7889      * Resets this form.
7890      * @return {BasicForm} this
7891      */
7892     reset : function(){
7893         var items = this.getItems();
7894         items.each(function(f){
7895             f.reset();
7896         });
7897
7898         Roo.each(this.childForms || [], function (f) {
7899             f.reset();
7900         });
7901
7902
7903         return this;
7904     },
7905     getItems : function()
7906     {
7907         var r=new Roo.util.MixedCollection(false, function(o){
7908             return o.id || (o.id = Roo.id());
7909         });
7910         var iter = function(el) {
7911             if (el.inputEl) {
7912                 r.add(el);
7913             }
7914             if (!el.items) {
7915                 return;
7916             }
7917             Roo.each(el.items,function(e) {
7918                 iter(e);
7919             });
7920
7921
7922         };
7923
7924         iter(this);
7925         return r;
7926
7927
7928
7929
7930     }
7931
7932 });
7933 /*
7934  * Based on:
7935  * Ext JS Library 1.1.1
7936  * Copyright(c) 2006-2007, Ext JS, LLC.
7937  *
7938  * Originally Released Under LGPL - original licence link has changed is not relivant.
7939  *
7940  * Fork - LGPL
7941  * <script type="text/javascript">
7942  */
7943 /**
7944  * @class Roo.form.VTypes
7945  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7946  * @singleton
7947  */
7948 Roo.form.VTypes = function(){
7949     // closure these in so they are only created once.
7950     var alpha = /^[a-zA-Z_]+$/;
7951     var alphanum = /^[a-zA-Z0-9_]+$/;
7952     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7953     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7954
7955     // All these messages and functions are configurable
7956     return {
7957         /**
7958          * The function used to validate email addresses
7959          * @param {String} value The email address
7960          */
7961         'email' : function(v){
7962             return email.test(v);
7963         },
7964         /**
7965          * The error text to display when the email validation function returns false
7966          * @type String
7967          */
7968         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7969         /**
7970          * The keystroke filter mask to be applied on email input
7971          * @type RegExp
7972          */
7973         'emailMask' : /[a-z0-9_\.\-@]/i,
7974
7975         /**
7976          * The function used to validate URLs
7977          * @param {String} value The URL
7978          */
7979         'url' : function(v){
7980             return url.test(v);
7981         },
7982         /**
7983          * The error text to display when the url validation function returns false
7984          * @type String
7985          */
7986         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7987         
7988         /**
7989          * The function used to validate alpha values
7990          * @param {String} value The value
7991          */
7992         'alpha' : function(v){
7993             return alpha.test(v);
7994         },
7995         /**
7996          * The error text to display when the alpha validation function returns false
7997          * @type String
7998          */
7999         'alphaText' : 'This field should only contain letters and _',
8000         /**
8001          * The keystroke filter mask to be applied on alpha input
8002          * @type RegExp
8003          */
8004         'alphaMask' : /[a-z_]/i,
8005
8006         /**
8007          * The function used to validate alphanumeric values
8008          * @param {String} value The value
8009          */
8010         'alphanum' : function(v){
8011             return alphanum.test(v);
8012         },
8013         /**
8014          * The error text to display when the alphanumeric validation function returns false
8015          * @type String
8016          */
8017         'alphanumText' : 'This field should only contain letters, numbers and _',
8018         /**
8019          * The keystroke filter mask to be applied on alphanumeric input
8020          * @type RegExp
8021          */
8022         'alphanumMask' : /[a-z0-9_]/i
8023     };
8024 }();/*
8025  * - LGPL
8026  *
8027  * Input
8028  * 
8029  */
8030
8031 /**
8032  * @class Roo.bootstrap.Input
8033  * @extends Roo.bootstrap.Component
8034  * Bootstrap Input class
8035  * @cfg {Boolean} disabled is it disabled
8036  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8037  * @cfg {String} name name of the input
8038  * @cfg {string} fieldLabel - the label associated
8039  * @cfg {string} placeholder - placeholder to put in text.
8040  * @cfg {string}  before - input group add on before
8041  * @cfg {string} after - input group add on after
8042  * @cfg {string} size - (lg|sm) or leave empty..
8043  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8044  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8045  * @cfg {Number} md colspan out of 12 for computer-sized screens
8046  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8047  * @cfg {string} value default value of the input
8048  * @cfg {Number} labelWidth set the width of label (0-12)
8049  * @cfg {String} labelAlign (top|left)
8050  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8051  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8052  * @cfg {String} indicatorpos (left|right) default left
8053
8054  * @cfg {String} align (left|center|right) Default left
8055  * @cfg {Boolean} forceFeedback (true|false) Default false
8056  * 
8057  * 
8058  * 
8059  * 
8060  * @constructor
8061  * Create a new Input
8062  * @param {Object} config The config object
8063  */
8064
8065 Roo.bootstrap.Input = function(config){
8066     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8067    
8068         this.addEvents({
8069             /**
8070              * @event focus
8071              * Fires when this field receives input focus.
8072              * @param {Roo.form.Field} this
8073              */
8074             focus : true,
8075             /**
8076              * @event blur
8077              * Fires when this field loses input focus.
8078              * @param {Roo.form.Field} this
8079              */
8080             blur : true,
8081             /**
8082              * @event specialkey
8083              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8084              * {@link Roo.EventObject#getKey} to determine which key was pressed.
8085              * @param {Roo.form.Field} this
8086              * @param {Roo.EventObject} e The event object
8087              */
8088             specialkey : true,
8089             /**
8090              * @event change
8091              * Fires just before the field blurs if the field value has changed.
8092              * @param {Roo.form.Field} this
8093              * @param {Mixed} newValue The new value
8094              * @param {Mixed} oldValue The original value
8095              */
8096             change : true,
8097             /**
8098              * @event invalid
8099              * Fires after the field has been marked as invalid.
8100              * @param {Roo.form.Field} this
8101              * @param {String} msg The validation message
8102              */
8103             invalid : true,
8104             /**
8105              * @event valid
8106              * Fires after the field has been validated with no errors.
8107              * @param {Roo.form.Field} this
8108              */
8109             valid : true,
8110              /**
8111              * @event keyup
8112              * Fires after the key up
8113              * @param {Roo.form.Field} this
8114              * @param {Roo.EventObject}  e The event Object
8115              */
8116             keyup : true
8117         });
8118 };
8119
8120 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8121      /**
8122      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8123       automatic validation (defaults to "keyup").
8124      */
8125     validationEvent : "keyup",
8126      /**
8127      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8128      */
8129     validateOnBlur : true,
8130     /**
8131      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8132      */
8133     validationDelay : 250,
8134      /**
8135      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8136      */
8137     focusClass : "x-form-focus",  // not needed???
8138     
8139        
8140     /**
8141      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8142      */
8143     invalidClass : "has-warning",
8144     
8145     /**
8146      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8147      */
8148     validClass : "has-success",
8149     
8150     /**
8151      * @cfg {Boolean} hasFeedback (true|false) default true
8152      */
8153     hasFeedback : true,
8154     
8155     /**
8156      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8157      */
8158     invalidFeedbackClass : "glyphicon-warning-sign",
8159     
8160     /**
8161      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8162      */
8163     validFeedbackClass : "glyphicon-ok",
8164     
8165     /**
8166      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8167      */
8168     selectOnFocus : false,
8169     
8170      /**
8171      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8172      */
8173     maskRe : null,
8174        /**
8175      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8176      */
8177     vtype : null,
8178     
8179       /**
8180      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8181      */
8182     disableKeyFilter : false,
8183     
8184        /**
8185      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8186      */
8187     disabled : false,
8188      /**
8189      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8190      */
8191     allowBlank : true,
8192     /**
8193      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8194      */
8195     blankText : "This field is required",
8196     
8197      /**
8198      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8199      */
8200     minLength : 0,
8201     /**
8202      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8203      */
8204     maxLength : Number.MAX_VALUE,
8205     /**
8206      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8207      */
8208     minLengthText : "The minimum length for this field is {0}",
8209     /**
8210      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8211      */
8212     maxLengthText : "The maximum length for this field is {0}",
8213   
8214     
8215     /**
8216      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8217      * If available, this function will be called only after the basic validators all return true, and will be passed the
8218      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8219      */
8220     validator : null,
8221     /**
8222      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8223      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8224      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8225      */
8226     regex : null,
8227     /**
8228      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8229      */
8230     regexText : "",
8231     
8232     autocomplete: false,
8233     
8234     
8235     fieldLabel : '',
8236     inputType : 'text',
8237     
8238     name : false,
8239     placeholder: false,
8240     before : false,
8241     after : false,
8242     size : false,
8243     hasFocus : false,
8244     preventMark: false,
8245     isFormField : true,
8246     value : '',
8247     labelWidth : 2,
8248     labelAlign : false,
8249     readOnly : false,
8250     align : false,
8251     formatedValue : false,
8252     forceFeedback : false,
8253     
8254     indicatorpos : 'left',
8255     
8256     parentLabelAlign : function()
8257     {
8258         var parent = this;
8259         while (parent.parent()) {
8260             parent = parent.parent();
8261             if (typeof(parent.labelAlign) !='undefined') {
8262                 return parent.labelAlign;
8263             }
8264         }
8265         return 'left';
8266         
8267     },
8268     
8269     getAutoCreate : function()
8270     {
8271         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8272         
8273         var id = Roo.id();
8274         
8275         var cfg = {};
8276         
8277         if(this.inputType != 'hidden'){
8278             cfg.cls = 'form-group' //input-group
8279         }
8280         
8281         var input =  {
8282             tag: 'input',
8283             id : id,
8284             type : this.inputType,
8285             value : this.value,
8286             cls : 'form-control',
8287             placeholder : this.placeholder || '',
8288             autocomplete : this.autocomplete || 'new-password'
8289         };
8290         
8291         if(this.align){
8292             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8293         }
8294         
8295         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8296             input.maxLength = this.maxLength;
8297         }
8298         
8299         if (this.disabled) {
8300             input.disabled=true;
8301         }
8302         
8303         if (this.readOnly) {
8304             input.readonly=true;
8305         }
8306         
8307         if (this.name) {
8308             input.name = this.name;
8309         }
8310         
8311         if (this.size) {
8312             input.cls += ' input-' + this.size;
8313         }
8314         
8315         var settings=this;
8316         ['xs','sm','md','lg'].map(function(size){
8317             if (settings[size]) {
8318                 cfg.cls += ' col-' + size + '-' + settings[size];
8319             }
8320         });
8321         
8322         var inputblock = input;
8323         
8324         var feedback = {
8325             tag: 'span',
8326             cls: 'glyphicon form-control-feedback'
8327         };
8328             
8329         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8330             
8331             inputblock = {
8332                 cls : 'has-feedback',
8333                 cn :  [
8334                     input,
8335                     feedback
8336                 ] 
8337             };  
8338         }
8339         
8340         if (this.before || this.after) {
8341             
8342             inputblock = {
8343                 cls : 'input-group',
8344                 cn :  [] 
8345             };
8346             
8347             if (this.before && typeof(this.before) == 'string') {
8348                 
8349                 inputblock.cn.push({
8350                     tag :'span',
8351                     cls : 'roo-input-before input-group-addon',
8352                     html : this.before
8353                 });
8354             }
8355             if (this.before && typeof(this.before) == 'object') {
8356                 this.before = Roo.factory(this.before);
8357                 
8358                 inputblock.cn.push({
8359                     tag :'span',
8360                     cls : 'roo-input-before input-group-' +
8361                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8362                 });
8363             }
8364             
8365             inputblock.cn.push(input);
8366             
8367             if (this.after && typeof(this.after) == 'string') {
8368                 inputblock.cn.push({
8369                     tag :'span',
8370                     cls : 'roo-input-after input-group-addon',
8371                     html : this.after
8372                 });
8373             }
8374             if (this.after && typeof(this.after) == 'object') {
8375                 this.after = Roo.factory(this.after);
8376                 
8377                 inputblock.cn.push({
8378                     tag :'span',
8379                     cls : 'roo-input-after input-group-' +
8380                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8381                 });
8382             }
8383             
8384             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8385                 inputblock.cls += ' has-feedback';
8386                 inputblock.cn.push(feedback);
8387             }
8388         };
8389         
8390         if (align ==='left' && this.fieldLabel.length) {
8391             
8392             cfg.cn = [
8393                 {
8394                     tag : 'i',
8395                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8396                     tooltip : 'This field is required'
8397                 },
8398                 {
8399                     tag: 'label',
8400                     'for' :  id,
8401                     cls : 'control-label col-sm-' + this.labelWidth,
8402                     html : this.fieldLabel
8403
8404                 },
8405                 {
8406                     cls : "col-sm-" + (12 - this.labelWidth), 
8407                     cn: [
8408                         inputblock
8409                     ]
8410                 }
8411
8412             ];
8413             
8414             if(this.indicatorpos == 'right'){
8415                 cfg.cn = [
8416                     {
8417                         tag: 'label',
8418                         'for' :  id,
8419                         cls : 'control-label col-sm-' + this.labelWidth,
8420                         html : this.fieldLabel
8421
8422                     },
8423                     {
8424                         tag : 'i',
8425                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8426                         tooltip : 'This field is required'
8427                     },
8428                     {
8429                         cls : "col-sm-" + (12 - this.labelWidth), 
8430                         cn: [
8431                             inputblock
8432                         ]
8433                     }
8434
8435                 ];
8436             }
8437             
8438         } else if ( this.fieldLabel.length) {
8439                 
8440             cfg.cn = [
8441                 {
8442                     tag : 'i',
8443                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8444                     tooltip : 'This field is required'
8445                 },
8446                 {
8447                     tag: 'label',
8448                    //cls : 'input-group-addon',
8449                     html : this.fieldLabel
8450
8451                 },
8452
8453                inputblock
8454
8455            ];
8456            
8457            if(this.indicatorpos == 'right'){
8458                 
8459                 cfg.cn = [
8460                     {
8461                         tag: 'label',
8462                        //cls : 'input-group-addon',
8463                         html : this.fieldLabel
8464
8465                     },
8466                     {
8467                         tag : 'i',
8468                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8469                         tooltip : 'This field is required'
8470                     },
8471
8472                    inputblock
8473
8474                ];
8475
8476             }
8477
8478         } else {
8479             
8480             cfg.cn = [
8481
8482                     inputblock
8483
8484             ];
8485                 
8486                 
8487         };
8488         
8489         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8490            cfg.cls += ' navbar-form';
8491         }
8492         
8493         if (this.parentType === 'NavGroup') {
8494            cfg.cls += ' navbar-form';
8495            cfg.tag = 'li';
8496         }
8497         
8498         return cfg;
8499         
8500     },
8501     /**
8502      * return the real input element.
8503      */
8504     inputEl: function ()
8505     {
8506         return this.el.select('input.form-control',true).first();
8507     },
8508     
8509     tooltipEl : function()
8510     {
8511         return this.inputEl();
8512     },
8513     
8514     indicatorEl : function()
8515     {
8516         var indicator = this.el.select('i.roo-required-indicator',true).first();
8517         
8518         if(!indicator){
8519             return false;
8520         }
8521         
8522         return indicator;
8523         
8524     },
8525     
8526     setDisabled : function(v)
8527     {
8528         var i  = this.inputEl().dom;
8529         if (!v) {
8530             i.removeAttribute('disabled');
8531             return;
8532             
8533         }
8534         i.setAttribute('disabled','true');
8535     },
8536     initEvents : function()
8537     {
8538           
8539         this.inputEl().on("keydown" , this.fireKey,  this);
8540         this.inputEl().on("focus", this.onFocus,  this);
8541         this.inputEl().on("blur", this.onBlur,  this);
8542         
8543         this.inputEl().relayEvent('keyup', this);
8544         
8545         this.indicator = this.indicatorEl();
8546         
8547         if(this.indicator){
8548             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8549             this.indicator.hide();
8550         }
8551  
8552         // reference to original value for reset
8553         this.originalValue = this.getValue();
8554         //Roo.form.TextField.superclass.initEvents.call(this);
8555         if(this.validationEvent == 'keyup'){
8556             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8557             this.inputEl().on('keyup', this.filterValidation, this);
8558         }
8559         else if(this.validationEvent !== false){
8560             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8561         }
8562         
8563         if(this.selectOnFocus){
8564             this.on("focus", this.preFocus, this);
8565             
8566         }
8567         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8568             this.inputEl().on("keypress", this.filterKeys, this);
8569         } else {
8570             this.inputEl().relayEvent('keypress', this);
8571         }
8572        /* if(this.grow){
8573             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8574             this.el.on("click", this.autoSize,  this);
8575         }
8576         */
8577         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8578             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8579         }
8580         
8581         if (typeof(this.before) == 'object') {
8582             this.before.render(this.el.select('.roo-input-before',true).first());
8583         }
8584         if (typeof(this.after) == 'object') {
8585             this.after.render(this.el.select('.roo-input-after',true).first());
8586         }
8587         
8588         
8589     },
8590     filterValidation : function(e){
8591         if(!e.isNavKeyPress()){
8592             this.validationTask.delay(this.validationDelay);
8593         }
8594     },
8595      /**
8596      * Validates the field value
8597      * @return {Boolean} True if the value is valid, else false
8598      */
8599     validate : function(){
8600         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8601         if(this.disabled || this.validateValue(this.getRawValue())){
8602             this.markValid();
8603             return true;
8604         }
8605         
8606         this.markInvalid();
8607         return false;
8608     },
8609     
8610     
8611     /**
8612      * Validates a value according to the field's validation rules and marks the field as invalid
8613      * if the validation fails
8614      * @param {Mixed} value The value to validate
8615      * @return {Boolean} True if the value is valid, else false
8616      */
8617     validateValue : function(value){
8618         if(value.length < 1)  { // if it's blank
8619             if(this.allowBlank){
8620                 return true;
8621             }
8622             return false;
8623         }
8624         
8625         if(value.length < this.minLength){
8626             return false;
8627         }
8628         if(value.length > this.maxLength){
8629             return false;
8630         }
8631         if(this.vtype){
8632             var vt = Roo.form.VTypes;
8633             if(!vt[this.vtype](value, this)){
8634                 return false;
8635             }
8636         }
8637         if(typeof this.validator == "function"){
8638             var msg = this.validator(value);
8639             if(msg !== true){
8640                 return false;
8641             }
8642         }
8643         
8644         if(this.regex && !this.regex.test(value)){
8645             return false;
8646         }
8647         
8648         return true;
8649     },
8650
8651     
8652     
8653      // private
8654     fireKey : function(e){
8655         //Roo.log('field ' + e.getKey());
8656         if(e.isNavKeyPress()){
8657             this.fireEvent("specialkey", this, e);
8658         }
8659     },
8660     focus : function (selectText){
8661         if(this.rendered){
8662             this.inputEl().focus();
8663             if(selectText === true){
8664                 this.inputEl().dom.select();
8665             }
8666         }
8667         return this;
8668     } ,
8669     
8670     onFocus : function(){
8671         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8672            // this.el.addClass(this.focusClass);
8673         }
8674         if(!this.hasFocus){
8675             this.hasFocus = true;
8676             this.startValue = this.getValue();
8677             this.fireEvent("focus", this);
8678         }
8679     },
8680     
8681     beforeBlur : Roo.emptyFn,
8682
8683     
8684     // private
8685     onBlur : function(){
8686         this.beforeBlur();
8687         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8688             //this.el.removeClass(this.focusClass);
8689         }
8690         this.hasFocus = false;
8691         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8692             this.validate();
8693         }
8694         var v = this.getValue();
8695         if(String(v) !== String(this.startValue)){
8696             this.fireEvent('change', this, v, this.startValue);
8697         }
8698         this.fireEvent("blur", this);
8699     },
8700     
8701     /**
8702      * Resets the current field value to the originally loaded value and clears any validation messages
8703      */
8704     reset : function(){
8705         this.setValue(this.originalValue);
8706         this.validate();
8707     },
8708      /**
8709      * Returns the name of the field
8710      * @return {Mixed} name The name field
8711      */
8712     getName: function(){
8713         return this.name;
8714     },
8715      /**
8716      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8717      * @return {Mixed} value The field value
8718      */
8719     getValue : function(){
8720         
8721         var v = this.inputEl().getValue();
8722         
8723         return v;
8724     },
8725     /**
8726      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8727      * @return {Mixed} value The field value
8728      */
8729     getRawValue : function(){
8730         var v = this.inputEl().getValue();
8731         
8732         return v;
8733     },
8734     
8735     /**
8736      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8737      * @param {Mixed} value The value to set
8738      */
8739     setRawValue : function(v){
8740         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8741     },
8742     
8743     selectText : function(start, end){
8744         var v = this.getRawValue();
8745         if(v.length > 0){
8746             start = start === undefined ? 0 : start;
8747             end = end === undefined ? v.length : end;
8748             var d = this.inputEl().dom;
8749             if(d.setSelectionRange){
8750                 d.setSelectionRange(start, end);
8751             }else if(d.createTextRange){
8752                 var range = d.createTextRange();
8753                 range.moveStart("character", start);
8754                 range.moveEnd("character", v.length-end);
8755                 range.select();
8756             }
8757         }
8758     },
8759     
8760     /**
8761      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8762      * @param {Mixed} value The value to set
8763      */
8764     setValue : function(v){
8765         this.value = v;
8766         if(this.rendered){
8767             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8768             this.validate();
8769         }
8770     },
8771     
8772     /*
8773     processValue : function(value){
8774         if(this.stripCharsRe){
8775             var newValue = value.replace(this.stripCharsRe, '');
8776             if(newValue !== value){
8777                 this.setRawValue(newValue);
8778                 return newValue;
8779             }
8780         }
8781         return value;
8782     },
8783   */
8784     preFocus : function(){
8785         
8786         if(this.selectOnFocus){
8787             this.inputEl().dom.select();
8788         }
8789     },
8790     filterKeys : function(e){
8791         var k = e.getKey();
8792         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8793             return;
8794         }
8795         var c = e.getCharCode(), cc = String.fromCharCode(c);
8796         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8797             return;
8798         }
8799         if(!this.maskRe.test(cc)){
8800             e.stopEvent();
8801         }
8802     },
8803      /**
8804      * Clear any invalid styles/messages for this field
8805      */
8806     clearInvalid : function(){
8807         
8808         if(!this.el || this.preventMark){ // not rendered
8809             return;
8810         }
8811         
8812         if(this.indicator){
8813             this.indicator.hide();
8814         }
8815         
8816         this.el.removeClass(this.invalidClass);
8817         
8818         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8819             
8820             var feedback = this.el.select('.form-control-feedback', true).first();
8821             
8822             if(feedback){
8823                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8824             }
8825             
8826         }
8827         
8828         this.fireEvent('valid', this);
8829     },
8830     
8831      /**
8832      * Mark this field as valid
8833      */
8834     markValid : function()
8835     {
8836         if(!this.el  || this.preventMark){ // not rendered
8837             return;
8838         }
8839         
8840         this.el.removeClass([this.invalidClass, this.validClass]);
8841         
8842         var feedback = this.el.select('.form-control-feedback', true).first();
8843             
8844         if(feedback){
8845             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8846         }
8847
8848         if(this.disabled){
8849             return;
8850         }
8851         
8852         if(this.allowBlank && !this.getRawValue().length){
8853             return;
8854         }
8855         
8856         if(this.indicator){
8857             this.indicator.hide();
8858         }
8859         
8860         this.el.addClass(this.validClass);
8861         
8862         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8863             
8864             var feedback = this.el.select('.form-control-feedback', true).first();
8865             
8866             if(feedback){
8867                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8868                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8869             }
8870             
8871         }
8872         
8873         this.fireEvent('valid', this);
8874     },
8875     
8876      /**
8877      * Mark this field as invalid
8878      * @param {String} msg The validation message
8879      */
8880     markInvalid : function(msg)
8881     {
8882         if(!this.el  || this.preventMark){ // not rendered
8883             return;
8884         }
8885         
8886         this.el.removeClass([this.invalidClass, this.validClass]);
8887         
8888         var feedback = this.el.select('.form-control-feedback', true).first();
8889             
8890         if(feedback){
8891             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8892         }
8893
8894         if(this.disabled){
8895             return;
8896         }
8897         
8898         if(this.allowBlank && !this.getRawValue().length){
8899             return;
8900         }
8901         
8902         if(this.indicator){
8903             this.indicator.show();
8904         }
8905         
8906         this.el.addClass(this.invalidClass);
8907         
8908         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8909             
8910             var feedback = this.el.select('.form-control-feedback', true).first();
8911             
8912             if(feedback){
8913                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8914                 
8915                 if(this.getValue().length || this.forceFeedback){
8916                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8917                 }
8918                 
8919             }
8920             
8921         }
8922         
8923         this.fireEvent('invalid', this, msg);
8924     },
8925     // private
8926     SafariOnKeyDown : function(event)
8927     {
8928         // this is a workaround for a password hang bug on chrome/ webkit.
8929         if (this.inputEl().dom.type != 'password') {
8930             return;
8931         }
8932         
8933         var isSelectAll = false;
8934         
8935         if(this.inputEl().dom.selectionEnd > 0){
8936             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8937         }
8938         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8939             event.preventDefault();
8940             this.setValue('');
8941             return;
8942         }
8943         
8944         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
8945             
8946             event.preventDefault();
8947             // this is very hacky as keydown always get's upper case.
8948             //
8949             var cc = String.fromCharCode(event.getCharCode());
8950             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8951             
8952         }
8953     },
8954     adjustWidth : function(tag, w){
8955         tag = tag.toLowerCase();
8956         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8957             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8958                 if(tag == 'input'){
8959                     return w + 2;
8960                 }
8961                 if(tag == 'textarea'){
8962                     return w-2;
8963                 }
8964             }else if(Roo.isOpera){
8965                 if(tag == 'input'){
8966                     return w + 2;
8967                 }
8968                 if(tag == 'textarea'){
8969                     return w-2;
8970                 }
8971             }
8972         }
8973         return w;
8974     }
8975     
8976 });
8977
8978  
8979 /*
8980  * - LGPL
8981  *
8982  * Input
8983  * 
8984  */
8985
8986 /**
8987  * @class Roo.bootstrap.TextArea
8988  * @extends Roo.bootstrap.Input
8989  * Bootstrap TextArea class
8990  * @cfg {Number} cols Specifies the visible width of a text area
8991  * @cfg {Number} rows Specifies the visible number of lines in a text area
8992  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8993  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8994  * @cfg {string} html text
8995  * 
8996  * @constructor
8997  * Create a new TextArea
8998  * @param {Object} config The config object
8999  */
9000
9001 Roo.bootstrap.TextArea = function(config){
9002     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9003    
9004 };
9005
9006 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9007      
9008     cols : false,
9009     rows : 5,
9010     readOnly : false,
9011     warp : 'soft',
9012     resize : false,
9013     value: false,
9014     html: false,
9015     
9016     getAutoCreate : function(){
9017         
9018         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9019         
9020         var id = Roo.id();
9021         
9022         var cfg = {};
9023         
9024         var input =  {
9025             tag: 'textarea',
9026             id : id,
9027             warp : this.warp,
9028             rows : this.rows,
9029             value : this.value || '',
9030             html: this.html || '',
9031             cls : 'form-control',
9032             placeholder : this.placeholder || '' 
9033             
9034         };
9035         
9036         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9037             input.maxLength = this.maxLength;
9038         }
9039         
9040         if(this.resize){
9041             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9042         }
9043         
9044         if(this.cols){
9045             input.cols = this.cols;
9046         }
9047         
9048         if (this.readOnly) {
9049             input.readonly = true;
9050         }
9051         
9052         if (this.name) {
9053             input.name = this.name;
9054         }
9055         
9056         if (this.size) {
9057             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9058         }
9059         
9060         var settings=this;
9061         ['xs','sm','md','lg'].map(function(size){
9062             if (settings[size]) {
9063                 cfg.cls += ' col-' + size + '-' + settings[size];
9064             }
9065         });
9066         
9067         var inputblock = input;
9068         
9069         if(this.hasFeedback && !this.allowBlank){
9070             
9071             var feedback = {
9072                 tag: 'span',
9073                 cls: 'glyphicon form-control-feedback'
9074             };
9075
9076             inputblock = {
9077                 cls : 'has-feedback',
9078                 cn :  [
9079                     input,
9080                     feedback
9081                 ] 
9082             };  
9083         }
9084         
9085         
9086         if (this.before || this.after) {
9087             
9088             inputblock = {
9089                 cls : 'input-group',
9090                 cn :  [] 
9091             };
9092             if (this.before) {
9093                 inputblock.cn.push({
9094                     tag :'span',
9095                     cls : 'input-group-addon',
9096                     html : this.before
9097                 });
9098             }
9099             
9100             inputblock.cn.push(input);
9101             
9102             if(this.hasFeedback && !this.allowBlank){
9103                 inputblock.cls += ' has-feedback';
9104                 inputblock.cn.push(feedback);
9105             }
9106             
9107             if (this.after) {
9108                 inputblock.cn.push({
9109                     tag :'span',
9110                     cls : 'input-group-addon',
9111                     html : this.after
9112                 });
9113             }
9114             
9115         }
9116         
9117         if (align ==='left' && this.fieldLabel.length) {
9118 //                Roo.log("left and has label");
9119                 cfg.cn = [
9120                     
9121                     {
9122                         tag: 'label',
9123                         'for' :  id,
9124                         cls : 'control-label col-sm-' + this.labelWidth,
9125                         html : this.fieldLabel
9126                         
9127                     },
9128                     {
9129                         cls : "col-sm-" + (12 - this.labelWidth), 
9130                         cn: [
9131                             inputblock
9132                         ]
9133                     }
9134                     
9135                 ];
9136         } else if ( this.fieldLabel.length) {
9137 //                Roo.log(" label");
9138                  cfg.cn = [
9139                    
9140                     {
9141                         tag: 'label',
9142                         //cls : 'input-group-addon',
9143                         html : this.fieldLabel
9144                         
9145                     },
9146                     
9147                     inputblock
9148                     
9149                 ];
9150
9151         } else {
9152             
9153 //                   Roo.log(" no label && no align");
9154                 cfg.cn = [
9155                     
9156                         inputblock
9157                     
9158                 ];
9159                 
9160                 
9161         }
9162         
9163         if (this.disabled) {
9164             input.disabled=true;
9165         }
9166         
9167         return cfg;
9168         
9169     },
9170     /**
9171      * return the real textarea element.
9172      */
9173     inputEl: function ()
9174     {
9175         return this.el.select('textarea.form-control',true).first();
9176     },
9177     
9178     /**
9179      * Clear any invalid styles/messages for this field
9180      */
9181     clearInvalid : function()
9182     {
9183         
9184         if(!this.el || this.preventMark){ // not rendered
9185             return;
9186         }
9187         
9188         var label = this.el.select('label', true).first();
9189         var icon = this.el.select('i.fa-star', true).first();
9190         
9191         if(label && icon){
9192             icon.remove();
9193         }
9194         
9195         this.el.removeClass(this.invalidClass);
9196         
9197         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9198             
9199             var feedback = this.el.select('.form-control-feedback', true).first();
9200             
9201             if(feedback){
9202                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9203             }
9204             
9205         }
9206         
9207         this.fireEvent('valid', this);
9208     },
9209     
9210      /**
9211      * Mark this field as valid
9212      */
9213     markValid : function()
9214     {
9215         if(!this.el  || this.preventMark){ // not rendered
9216             return;
9217         }
9218         
9219         this.el.removeClass([this.invalidClass, this.validClass]);
9220         
9221         var feedback = this.el.select('.form-control-feedback', true).first();
9222             
9223         if(feedback){
9224             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9225         }
9226
9227         if(this.disabled || this.allowBlank){
9228             return;
9229         }
9230         
9231         var label = this.el.select('label', true).first();
9232         var icon = this.el.select('i.fa-star', true).first();
9233         
9234         if(label && icon){
9235             icon.remove();
9236         }
9237         
9238         this.el.addClass(this.validClass);
9239         
9240         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9241             
9242             var feedback = this.el.select('.form-control-feedback', true).first();
9243             
9244             if(feedback){
9245                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9246                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9247             }
9248             
9249         }
9250         
9251         this.fireEvent('valid', this);
9252     },
9253     
9254      /**
9255      * Mark this field as invalid
9256      * @param {String} msg The validation message
9257      */
9258     markInvalid : function(msg)
9259     {
9260         if(!this.el  || this.preventMark){ // not rendered
9261             return;
9262         }
9263         
9264         this.el.removeClass([this.invalidClass, this.validClass]);
9265         
9266         var feedback = this.el.select('.form-control-feedback', true).first();
9267             
9268         if(feedback){
9269             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9270         }
9271
9272         if(this.disabled || this.allowBlank){
9273             return;
9274         }
9275         
9276         var label = this.el.select('label', true).first();
9277         var icon = this.el.select('i.fa-star', true).first();
9278         
9279         if(!this.getValue().length && label && !icon){
9280             this.el.createChild({
9281                 tag : 'i',
9282                 cls : 'text-danger fa fa-lg fa-star',
9283                 tooltip : 'This field is required',
9284                 style : 'margin-right:5px;'
9285             }, label, true);
9286         }
9287
9288         this.el.addClass(this.invalidClass);
9289         
9290         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9291             
9292             var feedback = this.el.select('.form-control-feedback', true).first();
9293             
9294             if(feedback){
9295                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9296                 
9297                 if(this.getValue().length || this.forceFeedback){
9298                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9299                 }
9300                 
9301             }
9302             
9303         }
9304         
9305         this.fireEvent('invalid', this, msg);
9306     }
9307 });
9308
9309  
9310 /*
9311  * - LGPL
9312  *
9313  * trigger field - base class for combo..
9314  * 
9315  */
9316  
9317 /**
9318  * @class Roo.bootstrap.TriggerField
9319  * @extends Roo.bootstrap.Input
9320  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9321  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9322  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9323  * for which you can provide a custom implementation.  For example:
9324  * <pre><code>
9325 var trigger = new Roo.bootstrap.TriggerField();
9326 trigger.onTriggerClick = myTriggerFn;
9327 trigger.applyTo('my-field');
9328 </code></pre>
9329  *
9330  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9331  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9332  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9333  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9334  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9335
9336  * @constructor
9337  * Create a new TriggerField.
9338  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9339  * to the base TextField)
9340  */
9341 Roo.bootstrap.TriggerField = function(config){
9342     this.mimicing = false;
9343     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9344 };
9345
9346 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9347     /**
9348      * @cfg {String} triggerClass A CSS class to apply to the trigger
9349      */
9350      /**
9351      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9352      */
9353     hideTrigger:false,
9354
9355     /**
9356      * @cfg {Boolean} removable (true|false) special filter default false
9357      */
9358     removable : false,
9359     
9360     /** @cfg {Boolean} grow @hide */
9361     /** @cfg {Number} growMin @hide */
9362     /** @cfg {Number} growMax @hide */
9363
9364     /**
9365      * @hide 
9366      * @method
9367      */
9368     autoSize: Roo.emptyFn,
9369     // private
9370     monitorTab : true,
9371     // private
9372     deferHeight : true,
9373
9374     
9375     actionMode : 'wrap',
9376     
9377     caret : false,
9378     
9379     
9380     getAutoCreate : function(){
9381        
9382         var align = this.labelAlign || this.parentLabelAlign();
9383         
9384         var id = Roo.id();
9385         
9386         var cfg = {
9387             cls: 'form-group' //input-group
9388         };
9389         
9390         
9391         var input =  {
9392             tag: 'input',
9393             id : id,
9394             type : this.inputType,
9395             cls : 'form-control',
9396             autocomplete: 'new-password',
9397             placeholder : this.placeholder || '' 
9398             
9399         };
9400         if (this.name) {
9401             input.name = this.name;
9402         }
9403         if (this.size) {
9404             input.cls += ' input-' + this.size;
9405         }
9406         
9407         if (this.disabled) {
9408             input.disabled=true;
9409         }
9410         
9411         var inputblock = input;
9412         
9413         if(this.hasFeedback && !this.allowBlank){
9414             
9415             var feedback = {
9416                 tag: 'span',
9417                 cls: 'glyphicon form-control-feedback'
9418             };
9419             
9420             if(this.removable && !this.editable && !this.tickable){
9421                 inputblock = {
9422                     cls : 'has-feedback',
9423                     cn :  [
9424                         inputblock,
9425                         {
9426                             tag: 'button',
9427                             html : 'x',
9428                             cls : 'roo-combo-removable-btn close'
9429                         },
9430                         feedback
9431                     ] 
9432                 };
9433             } else {
9434                 inputblock = {
9435                     cls : 'has-feedback',
9436                     cn :  [
9437                         inputblock,
9438                         feedback
9439                     ] 
9440                 };
9441             }
9442
9443         } else {
9444             if(this.removable && !this.editable && !this.tickable){
9445                 inputblock = {
9446                     cls : 'roo-removable',
9447                     cn :  [
9448                         inputblock,
9449                         {
9450                             tag: 'button',
9451                             html : 'x',
9452                             cls : 'roo-combo-removable-btn close'
9453                         }
9454                     ] 
9455                 };
9456             }
9457         }
9458         
9459         if (this.before || this.after) {
9460             
9461             inputblock = {
9462                 cls : 'input-group',
9463                 cn :  [] 
9464             };
9465             if (this.before) {
9466                 inputblock.cn.push({
9467                     tag :'span',
9468                     cls : 'input-group-addon',
9469                     html : this.before
9470                 });
9471             }
9472             
9473             inputblock.cn.push(input);
9474             
9475             if(this.hasFeedback && !this.allowBlank){
9476                 inputblock.cls += ' has-feedback';
9477                 inputblock.cn.push(feedback);
9478             }
9479             
9480             if (this.after) {
9481                 inputblock.cn.push({
9482                     tag :'span',
9483                     cls : 'input-group-addon',
9484                     html : this.after
9485                 });
9486             }
9487             
9488         };
9489         
9490         var box = {
9491             tag: 'div',
9492             cn: [
9493                 {
9494                     tag: 'input',
9495                     type : 'hidden',
9496                     cls: 'form-hidden-field'
9497                 },
9498                 inputblock
9499             ]
9500             
9501         };
9502         
9503         if(this.multiple){
9504             box = {
9505                 tag: 'div',
9506                 cn: [
9507                     {
9508                         tag: 'input',
9509                         type : 'hidden',
9510                         cls: 'form-hidden-field'
9511                     },
9512                     {
9513                         tag: 'ul',
9514                         cls: 'roo-select2-choices',
9515                         cn:[
9516                             {
9517                                 tag: 'li',
9518                                 cls: 'roo-select2-search-field',
9519                                 cn: [
9520
9521                                     inputblock
9522                                 ]
9523                             }
9524                         ]
9525                     }
9526                 ]
9527             }
9528         };
9529         
9530         var combobox = {
9531             cls: 'roo-select2-container input-group',
9532             cn: [
9533                 box
9534 //                {
9535 //                    tag: 'ul',
9536 //                    cls: 'typeahead typeahead-long dropdown-menu',
9537 //                    style: 'display:none'
9538 //                }
9539             ]
9540         };
9541         
9542         if(!this.multiple && this.showToggleBtn){
9543             
9544             var caret = {
9545                         tag: 'span',
9546                         cls: 'caret'
9547              };
9548             if (this.caret != false) {
9549                 caret = {
9550                      tag: 'i',
9551                      cls: 'fa fa-' + this.caret
9552                 };
9553                 
9554             }
9555             
9556             combobox.cn.push({
9557                 tag :'span',
9558                 cls : 'input-group-addon btn dropdown-toggle',
9559                 cn : [
9560                     caret,
9561                     {
9562                         tag: 'span',
9563                         cls: 'combobox-clear',
9564                         cn  : [
9565                             {
9566                                 tag : 'i',
9567                                 cls: 'icon-remove'
9568                             }
9569                         ]
9570                     }
9571                 ]
9572
9573             })
9574         }
9575         
9576         if(this.multiple){
9577             combobox.cls += ' roo-select2-container-multi';
9578         }
9579         
9580         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9581             
9582 //                Roo.log("left and has label");
9583             cfg.cn = [
9584                 {
9585                     tag : 'i',
9586                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9587                     tooltip : 'This field is required'
9588                 },
9589                 {
9590                     tag: 'label',
9591                     'for' :  id,
9592                     cls : 'control-label col-sm-' + this.labelWidth,
9593                     html : this.fieldLabel
9594
9595                 },
9596                 {
9597                     cls : "col-sm-" + (12 - this.labelWidth), 
9598                     cn: [
9599                         combobox
9600                     ]
9601                 }
9602
9603             ];
9604             
9605             if(this.indicatorpos == 'right'){
9606                 cfg.cn = [
9607                     {
9608                         tag: 'label',
9609                         'for' :  id,
9610                         cls : 'control-label col-sm-' + this.labelWidth,
9611                         html : this.fieldLabel
9612
9613                     },
9614                     {
9615                         tag : 'i',
9616                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9617                         tooltip : 'This field is required'
9618                     },
9619                     {
9620                         cls : "col-sm-" + (12 - this.labelWidth), 
9621                         cn: [
9622                             combobox
9623                         ]
9624                     }
9625
9626                 ];
9627             }
9628             
9629         } else if ( this.fieldLabel.length) {
9630 //                Roo.log(" label");
9631             cfg.cn = [
9632                 {
9633                    tag : 'i',
9634                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9635                    tooltip : 'This field is required'
9636                },
9637                {
9638                    tag: 'label',
9639                    //cls : 'input-group-addon',
9640                    html : this.fieldLabel
9641
9642                },
9643
9644                combobox
9645
9646             ];
9647             
9648             if(this.indicatorpos == 'right'){
9649                 
9650                 cfg.cn = [
9651                     {
9652                        tag: 'label',
9653                        //cls : 'input-group-addon',
9654                        html : this.fieldLabel
9655
9656                     },
9657                     {
9658                        tag : 'i',
9659                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9660                        tooltip : 'This field is required'
9661                     },
9662                     
9663                     combobox
9664
9665                 ];
9666
9667             }
9668
9669         } else {
9670             
9671 //                Roo.log(" no label && no align");
9672                 cfg = combobox
9673                      
9674                 
9675         }
9676          
9677         var settings=this;
9678         ['xs','sm','md','lg'].map(function(size){
9679             if (settings[size]) {
9680                 cfg.cls += ' col-' + size + '-' + settings[size];
9681             }
9682         });
9683         
9684         return cfg;
9685         
9686     },
9687     
9688     
9689     
9690     // private
9691     onResize : function(w, h){
9692 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9693 //        if(typeof w == 'number'){
9694 //            var x = w - this.trigger.getWidth();
9695 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9696 //            this.trigger.setStyle('left', x+'px');
9697 //        }
9698     },
9699
9700     // private
9701     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9702
9703     // private
9704     getResizeEl : function(){
9705         return this.inputEl();
9706     },
9707
9708     // private
9709     getPositionEl : function(){
9710         return this.inputEl();
9711     },
9712
9713     // private
9714     alignErrorIcon : function(){
9715         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9716     },
9717
9718     // private
9719     initEvents : function(){
9720         
9721         this.createList();
9722         
9723         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9724         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9725         if(!this.multiple && this.showToggleBtn){
9726             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9727             if(this.hideTrigger){
9728                 this.trigger.setDisplayed(false);
9729             }
9730             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9731         }
9732         
9733         if(this.multiple){
9734             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9735         }
9736         
9737         if(this.removable && !this.editable && !this.tickable){
9738             var close = this.closeTriggerEl();
9739             
9740             if(close){
9741                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9742                 close.on('click', this.removeBtnClick, this, close);
9743             }
9744         }
9745         
9746         //this.trigger.addClassOnOver('x-form-trigger-over');
9747         //this.trigger.addClassOnClick('x-form-trigger-click');
9748         
9749         //if(!this.width){
9750         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9751         //}
9752     },
9753     
9754     closeTriggerEl : function()
9755     {
9756         var close = this.el.select('.roo-combo-removable-btn', true).first();
9757         return close ? close : false;
9758     },
9759     
9760     removeBtnClick : function(e, h, el)
9761     {
9762         e.preventDefault();
9763         
9764         if(this.fireEvent("remove", this) !== false){
9765             this.reset();
9766             this.fireEvent("afterremove", this)
9767         }
9768     },
9769     
9770     createList : function()
9771     {
9772         this.list = Roo.get(document.body).createChild({
9773             tag: 'ul',
9774             cls: 'typeahead typeahead-long dropdown-menu',
9775             style: 'display:none'
9776         });
9777         
9778         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9779         
9780     },
9781
9782     // private
9783     initTrigger : function(){
9784        
9785     },
9786
9787     // private
9788     onDestroy : function(){
9789         if(this.trigger){
9790             this.trigger.removeAllListeners();
9791           //  this.trigger.remove();
9792         }
9793         //if(this.wrap){
9794         //    this.wrap.remove();
9795         //}
9796         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9797     },
9798
9799     // private
9800     onFocus : function(){
9801         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9802         /*
9803         if(!this.mimicing){
9804             this.wrap.addClass('x-trigger-wrap-focus');
9805             this.mimicing = true;
9806             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9807             if(this.monitorTab){
9808                 this.el.on("keydown", this.checkTab, this);
9809             }
9810         }
9811         */
9812     },
9813
9814     // private
9815     checkTab : function(e){
9816         if(e.getKey() == e.TAB){
9817             this.triggerBlur();
9818         }
9819     },
9820
9821     // private
9822     onBlur : function(){
9823         // do nothing
9824     },
9825
9826     // private
9827     mimicBlur : function(e, t){
9828         /*
9829         if(!this.wrap.contains(t) && this.validateBlur()){
9830             this.triggerBlur();
9831         }
9832         */
9833     },
9834
9835     // private
9836     triggerBlur : function(){
9837         this.mimicing = false;
9838         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9839         if(this.monitorTab){
9840             this.el.un("keydown", this.checkTab, this);
9841         }
9842         //this.wrap.removeClass('x-trigger-wrap-focus');
9843         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9844     },
9845
9846     // private
9847     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9848     validateBlur : function(e, t){
9849         return true;
9850     },
9851
9852     // private
9853     onDisable : function(){
9854         this.inputEl().dom.disabled = true;
9855         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9856         //if(this.wrap){
9857         //    this.wrap.addClass('x-item-disabled');
9858         //}
9859     },
9860
9861     // private
9862     onEnable : function(){
9863         this.inputEl().dom.disabled = false;
9864         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9865         //if(this.wrap){
9866         //    this.el.removeClass('x-item-disabled');
9867         //}
9868     },
9869
9870     // private
9871     onShow : function(){
9872         var ae = this.getActionEl();
9873         
9874         if(ae){
9875             ae.dom.style.display = '';
9876             ae.dom.style.visibility = 'visible';
9877         }
9878     },
9879
9880     // private
9881     
9882     onHide : function(){
9883         var ae = this.getActionEl();
9884         ae.dom.style.display = 'none';
9885     },
9886
9887     /**
9888      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9889      * by an implementing function.
9890      * @method
9891      * @param {EventObject} e
9892      */
9893     onTriggerClick : Roo.emptyFn
9894 });
9895  /*
9896  * Based on:
9897  * Ext JS Library 1.1.1
9898  * Copyright(c) 2006-2007, Ext JS, LLC.
9899  *
9900  * Originally Released Under LGPL - original licence link has changed is not relivant.
9901  *
9902  * Fork - LGPL
9903  * <script type="text/javascript">
9904  */
9905
9906
9907 /**
9908  * @class Roo.data.SortTypes
9909  * @singleton
9910  * Defines the default sorting (casting?) comparison functions used when sorting data.
9911  */
9912 Roo.data.SortTypes = {
9913     /**
9914      * Default sort that does nothing
9915      * @param {Mixed} s The value being converted
9916      * @return {Mixed} The comparison value
9917      */
9918     none : function(s){
9919         return s;
9920     },
9921     
9922     /**
9923      * The regular expression used to strip tags
9924      * @type {RegExp}
9925      * @property
9926      */
9927     stripTagsRE : /<\/?[^>]+>/gi,
9928     
9929     /**
9930      * Strips all HTML tags to sort on text only
9931      * @param {Mixed} s The value being converted
9932      * @return {String} The comparison value
9933      */
9934     asText : function(s){
9935         return String(s).replace(this.stripTagsRE, "");
9936     },
9937     
9938     /**
9939      * Strips all HTML tags to sort on text only - Case insensitive
9940      * @param {Mixed} s The value being converted
9941      * @return {String} The comparison value
9942      */
9943     asUCText : function(s){
9944         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9945     },
9946     
9947     /**
9948      * Case insensitive string
9949      * @param {Mixed} s The value being converted
9950      * @return {String} The comparison value
9951      */
9952     asUCString : function(s) {
9953         return String(s).toUpperCase();
9954     },
9955     
9956     /**
9957      * Date sorting
9958      * @param {Mixed} s The value being converted
9959      * @return {Number} The comparison value
9960      */
9961     asDate : function(s) {
9962         if(!s){
9963             return 0;
9964         }
9965         if(s instanceof Date){
9966             return s.getTime();
9967         }
9968         return Date.parse(String(s));
9969     },
9970     
9971     /**
9972      * Float sorting
9973      * @param {Mixed} s The value being converted
9974      * @return {Float} The comparison value
9975      */
9976     asFloat : function(s) {
9977         var val = parseFloat(String(s).replace(/,/g, ""));
9978         if(isNaN(val)) {
9979             val = 0;
9980         }
9981         return val;
9982     },
9983     
9984     /**
9985      * Integer sorting
9986      * @param {Mixed} s The value being converted
9987      * @return {Number} The comparison value
9988      */
9989     asInt : function(s) {
9990         var val = parseInt(String(s).replace(/,/g, ""));
9991         if(isNaN(val)) {
9992             val = 0;
9993         }
9994         return val;
9995     }
9996 };/*
9997  * Based on:
9998  * Ext JS Library 1.1.1
9999  * Copyright(c) 2006-2007, Ext JS, LLC.
10000  *
10001  * Originally Released Under LGPL - original licence link has changed is not relivant.
10002  *
10003  * Fork - LGPL
10004  * <script type="text/javascript">
10005  */
10006
10007 /**
10008 * @class Roo.data.Record
10009  * Instances of this class encapsulate both record <em>definition</em> information, and record
10010  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10011  * to access Records cached in an {@link Roo.data.Store} object.<br>
10012  * <p>
10013  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10014  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10015  * objects.<br>
10016  * <p>
10017  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10018  * @constructor
10019  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10020  * {@link #create}. The parameters are the same.
10021  * @param {Array} data An associative Array of data values keyed by the field name.
10022  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10023  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10024  * not specified an integer id is generated.
10025  */
10026 Roo.data.Record = function(data, id){
10027     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10028     this.data = data;
10029 };
10030
10031 /**
10032  * Generate a constructor for a specific record layout.
10033  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10034  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10035  * Each field definition object may contain the following properties: <ul>
10036  * <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,
10037  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10038  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10039  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10040  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10041  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10042  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10043  * this may be omitted.</p></li>
10044  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10045  * <ul><li>auto (Default, implies no conversion)</li>
10046  * <li>string</li>
10047  * <li>int</li>
10048  * <li>float</li>
10049  * <li>boolean</li>
10050  * <li>date</li></ul></p></li>
10051  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10052  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10053  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10054  * by the Reader into an object that will be stored in the Record. It is passed the
10055  * following parameters:<ul>
10056  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10057  * </ul></p></li>
10058  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10059  * </ul>
10060  * <br>usage:<br><pre><code>
10061 var TopicRecord = Roo.data.Record.create(
10062     {name: 'title', mapping: 'topic_title'},
10063     {name: 'author', mapping: 'username'},
10064     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10065     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10066     {name: 'lastPoster', mapping: 'user2'},
10067     {name: 'excerpt', mapping: 'post_text'}
10068 );
10069
10070 var myNewRecord = new TopicRecord({
10071     title: 'Do my job please',
10072     author: 'noobie',
10073     totalPosts: 1,
10074     lastPost: new Date(),
10075     lastPoster: 'Animal',
10076     excerpt: 'No way dude!'
10077 });
10078 myStore.add(myNewRecord);
10079 </code></pre>
10080  * @method create
10081  * @static
10082  */
10083 Roo.data.Record.create = function(o){
10084     var f = function(){
10085         f.superclass.constructor.apply(this, arguments);
10086     };
10087     Roo.extend(f, Roo.data.Record);
10088     var p = f.prototype;
10089     p.fields = new Roo.util.MixedCollection(false, function(field){
10090         return field.name;
10091     });
10092     for(var i = 0, len = o.length; i < len; i++){
10093         p.fields.add(new Roo.data.Field(o[i]));
10094     }
10095     f.getField = function(name){
10096         return p.fields.get(name);  
10097     };
10098     return f;
10099 };
10100
10101 Roo.data.Record.AUTO_ID = 1000;
10102 Roo.data.Record.EDIT = 'edit';
10103 Roo.data.Record.REJECT = 'reject';
10104 Roo.data.Record.COMMIT = 'commit';
10105
10106 Roo.data.Record.prototype = {
10107     /**
10108      * Readonly flag - true if this record has been modified.
10109      * @type Boolean
10110      */
10111     dirty : false,
10112     editing : false,
10113     error: null,
10114     modified: null,
10115
10116     // private
10117     join : function(store){
10118         this.store = store;
10119     },
10120
10121     /**
10122      * Set the named field to the specified value.
10123      * @param {String} name The name of the field to set.
10124      * @param {Object} value The value to set the field to.
10125      */
10126     set : function(name, value){
10127         if(this.data[name] == value){
10128             return;
10129         }
10130         this.dirty = true;
10131         if(!this.modified){
10132             this.modified = {};
10133         }
10134         if(typeof this.modified[name] == 'undefined'){
10135             this.modified[name] = this.data[name];
10136         }
10137         this.data[name] = value;
10138         if(!this.editing && this.store){
10139             this.store.afterEdit(this);
10140         }       
10141     },
10142
10143     /**
10144      * Get the value of the named field.
10145      * @param {String} name The name of the field to get the value of.
10146      * @return {Object} The value of the field.
10147      */
10148     get : function(name){
10149         return this.data[name]; 
10150     },
10151
10152     // private
10153     beginEdit : function(){
10154         this.editing = true;
10155         this.modified = {}; 
10156     },
10157
10158     // private
10159     cancelEdit : function(){
10160         this.editing = false;
10161         delete this.modified;
10162     },
10163
10164     // private
10165     endEdit : function(){
10166         this.editing = false;
10167         if(this.dirty && this.store){
10168             this.store.afterEdit(this);
10169         }
10170     },
10171
10172     /**
10173      * Usually called by the {@link Roo.data.Store} which owns the Record.
10174      * Rejects all changes made to the Record since either creation, or the last commit operation.
10175      * Modified fields are reverted to their original values.
10176      * <p>
10177      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10178      * of reject operations.
10179      */
10180     reject : function(){
10181         var m = this.modified;
10182         for(var n in m){
10183             if(typeof m[n] != "function"){
10184                 this.data[n] = m[n];
10185             }
10186         }
10187         this.dirty = false;
10188         delete this.modified;
10189         this.editing = false;
10190         if(this.store){
10191             this.store.afterReject(this);
10192         }
10193     },
10194
10195     /**
10196      * Usually called by the {@link Roo.data.Store} which owns the Record.
10197      * Commits all changes made to the Record since either creation, or the last commit operation.
10198      * <p>
10199      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10200      * of commit operations.
10201      */
10202     commit : function(){
10203         this.dirty = false;
10204         delete this.modified;
10205         this.editing = false;
10206         if(this.store){
10207             this.store.afterCommit(this);
10208         }
10209     },
10210
10211     // private
10212     hasError : function(){
10213         return this.error != null;
10214     },
10215
10216     // private
10217     clearError : function(){
10218         this.error = null;
10219     },
10220
10221     /**
10222      * Creates a copy of this record.
10223      * @param {String} id (optional) A new record id if you don't want to use this record's id
10224      * @return {Record}
10225      */
10226     copy : function(newId) {
10227         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10228     }
10229 };/*
10230  * Based on:
10231  * Ext JS Library 1.1.1
10232  * Copyright(c) 2006-2007, Ext JS, LLC.
10233  *
10234  * Originally Released Under LGPL - original licence link has changed is not relivant.
10235  *
10236  * Fork - LGPL
10237  * <script type="text/javascript">
10238  */
10239
10240
10241
10242 /**
10243  * @class Roo.data.Store
10244  * @extends Roo.util.Observable
10245  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10246  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10247  * <p>
10248  * 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
10249  * has no knowledge of the format of the data returned by the Proxy.<br>
10250  * <p>
10251  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10252  * instances from the data object. These records are cached and made available through accessor functions.
10253  * @constructor
10254  * Creates a new Store.
10255  * @param {Object} config A config object containing the objects needed for the Store to access data,
10256  * and read the data into Records.
10257  */
10258 Roo.data.Store = function(config){
10259     this.data = new Roo.util.MixedCollection(false);
10260     this.data.getKey = function(o){
10261         return o.id;
10262     };
10263     this.baseParams = {};
10264     // private
10265     this.paramNames = {
10266         "start" : "start",
10267         "limit" : "limit",
10268         "sort" : "sort",
10269         "dir" : "dir",
10270         "multisort" : "_multisort"
10271     };
10272
10273     if(config && config.data){
10274         this.inlineData = config.data;
10275         delete config.data;
10276     }
10277
10278     Roo.apply(this, config);
10279     
10280     if(this.reader){ // reader passed
10281         this.reader = Roo.factory(this.reader, Roo.data);
10282         this.reader.xmodule = this.xmodule || false;
10283         if(!this.recordType){
10284             this.recordType = this.reader.recordType;
10285         }
10286         if(this.reader.onMetaChange){
10287             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10288         }
10289     }
10290
10291     if(this.recordType){
10292         this.fields = this.recordType.prototype.fields;
10293     }
10294     this.modified = [];
10295
10296     this.addEvents({
10297         /**
10298          * @event datachanged
10299          * Fires when the data cache has changed, and a widget which is using this Store
10300          * as a Record cache should refresh its view.
10301          * @param {Store} this
10302          */
10303         datachanged : true,
10304         /**
10305          * @event metachange
10306          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10307          * @param {Store} this
10308          * @param {Object} meta The JSON metadata
10309          */
10310         metachange : true,
10311         /**
10312          * @event add
10313          * Fires when Records have been added to the Store
10314          * @param {Store} this
10315          * @param {Roo.data.Record[]} records The array of Records added
10316          * @param {Number} index The index at which the record(s) were added
10317          */
10318         add : true,
10319         /**
10320          * @event remove
10321          * Fires when a Record has been removed from the Store
10322          * @param {Store} this
10323          * @param {Roo.data.Record} record The Record that was removed
10324          * @param {Number} index The index at which the record was removed
10325          */
10326         remove : true,
10327         /**
10328          * @event update
10329          * Fires when a Record has been updated
10330          * @param {Store} this
10331          * @param {Roo.data.Record} record The Record that was updated
10332          * @param {String} operation The update operation being performed.  Value may be one of:
10333          * <pre><code>
10334  Roo.data.Record.EDIT
10335  Roo.data.Record.REJECT
10336  Roo.data.Record.COMMIT
10337          * </code></pre>
10338          */
10339         update : true,
10340         /**
10341          * @event clear
10342          * Fires when the data cache has been cleared.
10343          * @param {Store} this
10344          */
10345         clear : true,
10346         /**
10347          * @event beforeload
10348          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10349          * the load action will be canceled.
10350          * @param {Store} this
10351          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10352          */
10353         beforeload : true,
10354         /**
10355          * @event beforeloadadd
10356          * Fires after a new set of Records has been loaded.
10357          * @param {Store} this
10358          * @param {Roo.data.Record[]} records The Records that were loaded
10359          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10360          */
10361         beforeloadadd : true,
10362         /**
10363          * @event load
10364          * Fires after a new set of Records has been loaded, before they are added to the store.
10365          * @param {Store} this
10366          * @param {Roo.data.Record[]} records The Records that were loaded
10367          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10368          * @params {Object} return from reader
10369          */
10370         load : true,
10371         /**
10372          * @event loadexception
10373          * Fires if an exception occurs in the Proxy during loading.
10374          * Called with the signature of the Proxy's "loadexception" event.
10375          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10376          * 
10377          * @param {Proxy} 
10378          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10379          * @param {Object} load options 
10380          * @param {Object} jsonData from your request (normally this contains the Exception)
10381          */
10382         loadexception : true
10383     });
10384     
10385     if(this.proxy){
10386         this.proxy = Roo.factory(this.proxy, Roo.data);
10387         this.proxy.xmodule = this.xmodule || false;
10388         this.relayEvents(this.proxy,  ["loadexception"]);
10389     }
10390     this.sortToggle = {};
10391     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10392
10393     Roo.data.Store.superclass.constructor.call(this);
10394
10395     if(this.inlineData){
10396         this.loadData(this.inlineData);
10397         delete this.inlineData;
10398     }
10399 };
10400
10401 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10402      /**
10403     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10404     * without a remote query - used by combo/forms at present.
10405     */
10406     
10407     /**
10408     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10409     */
10410     /**
10411     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10412     */
10413     /**
10414     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10415     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10416     */
10417     /**
10418     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10419     * on any HTTP request
10420     */
10421     /**
10422     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10423     */
10424     /**
10425     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10426     */
10427     multiSort: false,
10428     /**
10429     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10430     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10431     */
10432     remoteSort : false,
10433
10434     /**
10435     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10436      * loaded or when a record is removed. (defaults to false).
10437     */
10438     pruneModifiedRecords : false,
10439
10440     // private
10441     lastOptions : null,
10442
10443     /**
10444      * Add Records to the Store and fires the add event.
10445      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10446      */
10447     add : function(records){
10448         records = [].concat(records);
10449         for(var i = 0, len = records.length; i < len; i++){
10450             records[i].join(this);
10451         }
10452         var index = this.data.length;
10453         this.data.addAll(records);
10454         this.fireEvent("add", this, records, index);
10455     },
10456
10457     /**
10458      * Remove a Record from the Store and fires the remove event.
10459      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10460      */
10461     remove : function(record){
10462         var index = this.data.indexOf(record);
10463         this.data.removeAt(index);
10464         if(this.pruneModifiedRecords){
10465             this.modified.remove(record);
10466         }
10467         this.fireEvent("remove", this, record, index);
10468     },
10469
10470     /**
10471      * Remove all Records from the Store and fires the clear event.
10472      */
10473     removeAll : function(){
10474         this.data.clear();
10475         if(this.pruneModifiedRecords){
10476             this.modified = [];
10477         }
10478         this.fireEvent("clear", this);
10479     },
10480
10481     /**
10482      * Inserts Records to the Store at the given index and fires the add event.
10483      * @param {Number} index The start index at which to insert the passed Records.
10484      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10485      */
10486     insert : function(index, records){
10487         records = [].concat(records);
10488         for(var i = 0, len = records.length; i < len; i++){
10489             this.data.insert(index, records[i]);
10490             records[i].join(this);
10491         }
10492         this.fireEvent("add", this, records, index);
10493     },
10494
10495     /**
10496      * Get the index within the cache of the passed Record.
10497      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10498      * @return {Number} The index of the passed Record. Returns -1 if not found.
10499      */
10500     indexOf : function(record){
10501         return this.data.indexOf(record);
10502     },
10503
10504     /**
10505      * Get the index within the cache of the Record with the passed id.
10506      * @param {String} id The id of the Record to find.
10507      * @return {Number} The index of the Record. Returns -1 if not found.
10508      */
10509     indexOfId : function(id){
10510         return this.data.indexOfKey(id);
10511     },
10512
10513     /**
10514      * Get the Record with the specified id.
10515      * @param {String} id The id of the Record to find.
10516      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10517      */
10518     getById : function(id){
10519         return this.data.key(id);
10520     },
10521
10522     /**
10523      * Get the Record at the specified index.
10524      * @param {Number} index The index of the Record to find.
10525      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10526      */
10527     getAt : function(index){
10528         return this.data.itemAt(index);
10529     },
10530
10531     /**
10532      * Returns a range of Records between specified indices.
10533      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10534      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10535      * @return {Roo.data.Record[]} An array of Records
10536      */
10537     getRange : function(start, end){
10538         return this.data.getRange(start, end);
10539     },
10540
10541     // private
10542     storeOptions : function(o){
10543         o = Roo.apply({}, o);
10544         delete o.callback;
10545         delete o.scope;
10546         this.lastOptions = o;
10547     },
10548
10549     /**
10550      * Loads the Record cache from the configured Proxy using the configured Reader.
10551      * <p>
10552      * If using remote paging, then the first load call must specify the <em>start</em>
10553      * and <em>limit</em> properties in the options.params property to establish the initial
10554      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10555      * <p>
10556      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10557      * and this call will return before the new data has been loaded. Perform any post-processing
10558      * in a callback function, or in a "load" event handler.</strong>
10559      * <p>
10560      * @param {Object} options An object containing properties which control loading options:<ul>
10561      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10562      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10563      * passed the following arguments:<ul>
10564      * <li>r : Roo.data.Record[]</li>
10565      * <li>options: Options object from the load call</li>
10566      * <li>success: Boolean success indicator</li></ul></li>
10567      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10568      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10569      * </ul>
10570      */
10571     load : function(options){
10572         options = options || {};
10573         if(this.fireEvent("beforeload", this, options) !== false){
10574             this.storeOptions(options);
10575             var p = Roo.apply(options.params || {}, this.baseParams);
10576             // if meta was not loaded from remote source.. try requesting it.
10577             if (!this.reader.metaFromRemote) {
10578                 p._requestMeta = 1;
10579             }
10580             if(this.sortInfo && this.remoteSort){
10581                 var pn = this.paramNames;
10582                 p[pn["sort"]] = this.sortInfo.field;
10583                 p[pn["dir"]] = this.sortInfo.direction;
10584             }
10585             if (this.multiSort) {
10586                 var pn = this.paramNames;
10587                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10588             }
10589             
10590             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10591         }
10592     },
10593
10594     /**
10595      * Reloads the Record cache from the configured Proxy using the configured Reader and
10596      * the options from the last load operation performed.
10597      * @param {Object} options (optional) An object containing properties which may override the options
10598      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10599      * the most recently used options are reused).
10600      */
10601     reload : function(options){
10602         this.load(Roo.applyIf(options||{}, this.lastOptions));
10603     },
10604
10605     // private
10606     // Called as a callback by the Reader during a load operation.
10607     loadRecords : function(o, options, success){
10608         if(!o || success === false){
10609             if(success !== false){
10610                 this.fireEvent("load", this, [], options, o);
10611             }
10612             if(options.callback){
10613                 options.callback.call(options.scope || this, [], options, false);
10614             }
10615             return;
10616         }
10617         // if data returned failure - throw an exception.
10618         if (o.success === false) {
10619             // show a message if no listener is registered.
10620             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10621                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10622             }
10623             // loadmask wil be hooked into this..
10624             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10625             return;
10626         }
10627         var r = o.records, t = o.totalRecords || r.length;
10628         
10629         this.fireEvent("beforeloadadd", this, r, options, o);
10630         
10631         if(!options || options.add !== true){
10632             if(this.pruneModifiedRecords){
10633                 this.modified = [];
10634             }
10635             for(var i = 0, len = r.length; i < len; i++){
10636                 r[i].join(this);
10637             }
10638             if(this.snapshot){
10639                 this.data = this.snapshot;
10640                 delete this.snapshot;
10641             }
10642             this.data.clear();
10643             this.data.addAll(r);
10644             this.totalLength = t;
10645             this.applySort();
10646             this.fireEvent("datachanged", this);
10647         }else{
10648             this.totalLength = Math.max(t, this.data.length+r.length);
10649             this.add(r);
10650         }
10651         this.fireEvent("load", this, r, options, o);
10652         if(options.callback){
10653             options.callback.call(options.scope || this, r, options, true);
10654         }
10655     },
10656
10657
10658     /**
10659      * Loads data from a passed data block. A Reader which understands the format of the data
10660      * must have been configured in the constructor.
10661      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10662      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10663      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10664      */
10665     loadData : function(o, append){
10666         var r = this.reader.readRecords(o);
10667         this.loadRecords(r, {add: append}, true);
10668     },
10669
10670     /**
10671      * Gets the number of cached records.
10672      * <p>
10673      * <em>If using paging, this may not be the total size of the dataset. If the data object
10674      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10675      * the data set size</em>
10676      */
10677     getCount : function(){
10678         return this.data.length || 0;
10679     },
10680
10681     /**
10682      * Gets the total number of records in the dataset as returned by the server.
10683      * <p>
10684      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10685      * the dataset size</em>
10686      */
10687     getTotalCount : function(){
10688         return this.totalLength || 0;
10689     },
10690
10691     /**
10692      * Returns the sort state of the Store as an object with two properties:
10693      * <pre><code>
10694  field {String} The name of the field by which the Records are sorted
10695  direction {String} The sort order, "ASC" or "DESC"
10696      * </code></pre>
10697      */
10698     getSortState : function(){
10699         return this.sortInfo;
10700     },
10701
10702     // private
10703     applySort : function(){
10704         if(this.sortInfo && !this.remoteSort){
10705             var s = this.sortInfo, f = s.field;
10706             var st = this.fields.get(f).sortType;
10707             var fn = function(r1, r2){
10708                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10709                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10710             };
10711             this.data.sort(s.direction, fn);
10712             if(this.snapshot && this.snapshot != this.data){
10713                 this.snapshot.sort(s.direction, fn);
10714             }
10715         }
10716     },
10717
10718     /**
10719      * Sets the default sort column and order to be used by the next load operation.
10720      * @param {String} fieldName The name of the field to sort by.
10721      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10722      */
10723     setDefaultSort : function(field, dir){
10724         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10725     },
10726
10727     /**
10728      * Sort the Records.
10729      * If remote sorting is used, the sort is performed on the server, and the cache is
10730      * reloaded. If local sorting is used, the cache is sorted internally.
10731      * @param {String} fieldName The name of the field to sort by.
10732      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10733      */
10734     sort : function(fieldName, dir){
10735         var f = this.fields.get(fieldName);
10736         if(!dir){
10737             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10738             
10739             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10740                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10741             }else{
10742                 dir = f.sortDir;
10743             }
10744         }
10745         this.sortToggle[f.name] = dir;
10746         this.sortInfo = {field: f.name, direction: dir};
10747         if(!this.remoteSort){
10748             this.applySort();
10749             this.fireEvent("datachanged", this);
10750         }else{
10751             this.load(this.lastOptions);
10752         }
10753     },
10754
10755     /**
10756      * Calls the specified function for each of the Records in the cache.
10757      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10758      * Returning <em>false</em> aborts and exits the iteration.
10759      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10760      */
10761     each : function(fn, scope){
10762         this.data.each(fn, scope);
10763     },
10764
10765     /**
10766      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10767      * (e.g., during paging).
10768      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10769      */
10770     getModifiedRecords : function(){
10771         return this.modified;
10772     },
10773
10774     // private
10775     createFilterFn : function(property, value, anyMatch){
10776         if(!value.exec){ // not a regex
10777             value = String(value);
10778             if(value.length == 0){
10779                 return false;
10780             }
10781             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10782         }
10783         return function(r){
10784             return value.test(r.data[property]);
10785         };
10786     },
10787
10788     /**
10789      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10790      * @param {String} property A field on your records
10791      * @param {Number} start The record index to start at (defaults to 0)
10792      * @param {Number} end The last record index to include (defaults to length - 1)
10793      * @return {Number} The sum
10794      */
10795     sum : function(property, start, end){
10796         var rs = this.data.items, v = 0;
10797         start = start || 0;
10798         end = (end || end === 0) ? end : rs.length-1;
10799
10800         for(var i = start; i <= end; i++){
10801             v += (rs[i].data[property] || 0);
10802         }
10803         return v;
10804     },
10805
10806     /**
10807      * Filter the records by a specified property.
10808      * @param {String} field A field on your records
10809      * @param {String/RegExp} value Either a string that the field
10810      * should start with or a RegExp to test against the field
10811      * @param {Boolean} anyMatch True to match any part not just the beginning
10812      */
10813     filter : function(property, value, anyMatch){
10814         var fn = this.createFilterFn(property, value, anyMatch);
10815         return fn ? this.filterBy(fn) : this.clearFilter();
10816     },
10817
10818     /**
10819      * Filter by a function. The specified function will be called with each
10820      * record in this data source. If the function returns true the record is included,
10821      * otherwise it is filtered.
10822      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10823      * @param {Object} scope (optional) The scope of the function (defaults to this)
10824      */
10825     filterBy : function(fn, scope){
10826         this.snapshot = this.snapshot || this.data;
10827         this.data = this.queryBy(fn, scope||this);
10828         this.fireEvent("datachanged", this);
10829     },
10830
10831     /**
10832      * Query the records by a specified property.
10833      * @param {String} field A field on your records
10834      * @param {String/RegExp} value Either a string that the field
10835      * should start with or a RegExp to test against the field
10836      * @param {Boolean} anyMatch True to match any part not just the beginning
10837      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10838      */
10839     query : function(property, value, anyMatch){
10840         var fn = this.createFilterFn(property, value, anyMatch);
10841         return fn ? this.queryBy(fn) : this.data.clone();
10842     },
10843
10844     /**
10845      * Query by a function. The specified function will be called with each
10846      * record in this data source. If the function returns true the record is included
10847      * in the results.
10848      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10849      * @param {Object} scope (optional) The scope of the function (defaults to this)
10850       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10851      **/
10852     queryBy : function(fn, scope){
10853         var data = this.snapshot || this.data;
10854         return data.filterBy(fn, scope||this);
10855     },
10856
10857     /**
10858      * Collects unique values for a particular dataIndex from this store.
10859      * @param {String} dataIndex The property to collect
10860      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10861      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10862      * @return {Array} An array of the unique values
10863      **/
10864     collect : function(dataIndex, allowNull, bypassFilter){
10865         var d = (bypassFilter === true && this.snapshot) ?
10866                 this.snapshot.items : this.data.items;
10867         var v, sv, r = [], l = {};
10868         for(var i = 0, len = d.length; i < len; i++){
10869             v = d[i].data[dataIndex];
10870             sv = String(v);
10871             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10872                 l[sv] = true;
10873                 r[r.length] = v;
10874             }
10875         }
10876         return r;
10877     },
10878
10879     /**
10880      * Revert to a view of the Record cache with no filtering applied.
10881      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10882      */
10883     clearFilter : function(suppressEvent){
10884         if(this.snapshot && this.snapshot != this.data){
10885             this.data = this.snapshot;
10886             delete this.snapshot;
10887             if(suppressEvent !== true){
10888                 this.fireEvent("datachanged", this);
10889             }
10890         }
10891     },
10892
10893     // private
10894     afterEdit : function(record){
10895         if(this.modified.indexOf(record) == -1){
10896             this.modified.push(record);
10897         }
10898         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10899     },
10900     
10901     // private
10902     afterReject : function(record){
10903         this.modified.remove(record);
10904         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10905     },
10906
10907     // private
10908     afterCommit : function(record){
10909         this.modified.remove(record);
10910         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10911     },
10912
10913     /**
10914      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10915      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10916      */
10917     commitChanges : function(){
10918         var m = this.modified.slice(0);
10919         this.modified = [];
10920         for(var i = 0, len = m.length; i < len; i++){
10921             m[i].commit();
10922         }
10923     },
10924
10925     /**
10926      * Cancel outstanding changes on all changed records.
10927      */
10928     rejectChanges : function(){
10929         var m = this.modified.slice(0);
10930         this.modified = [];
10931         for(var i = 0, len = m.length; i < len; i++){
10932             m[i].reject();
10933         }
10934     },
10935
10936     onMetaChange : function(meta, rtype, o){
10937         this.recordType = rtype;
10938         this.fields = rtype.prototype.fields;
10939         delete this.snapshot;
10940         this.sortInfo = meta.sortInfo || this.sortInfo;
10941         this.modified = [];
10942         this.fireEvent('metachange', this, this.reader.meta);
10943     },
10944     
10945     moveIndex : function(data, type)
10946     {
10947         var index = this.indexOf(data);
10948         
10949         var newIndex = index + type;
10950         
10951         this.remove(data);
10952         
10953         this.insert(newIndex, data);
10954         
10955     }
10956 });/*
10957  * Based on:
10958  * Ext JS Library 1.1.1
10959  * Copyright(c) 2006-2007, Ext JS, LLC.
10960  *
10961  * Originally Released Under LGPL - original licence link has changed is not relivant.
10962  *
10963  * Fork - LGPL
10964  * <script type="text/javascript">
10965  */
10966
10967 /**
10968  * @class Roo.data.SimpleStore
10969  * @extends Roo.data.Store
10970  * Small helper class to make creating Stores from Array data easier.
10971  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10972  * @cfg {Array} fields An array of field definition objects, or field name strings.
10973  * @cfg {Array} data The multi-dimensional array of data
10974  * @constructor
10975  * @param {Object} config
10976  */
10977 Roo.data.SimpleStore = function(config){
10978     Roo.data.SimpleStore.superclass.constructor.call(this, {
10979         isLocal : true,
10980         reader: new Roo.data.ArrayReader({
10981                 id: config.id
10982             },
10983             Roo.data.Record.create(config.fields)
10984         ),
10985         proxy : new Roo.data.MemoryProxy(config.data)
10986     });
10987     this.load();
10988 };
10989 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10990  * Based on:
10991  * Ext JS Library 1.1.1
10992  * Copyright(c) 2006-2007, Ext JS, LLC.
10993  *
10994  * Originally Released Under LGPL - original licence link has changed is not relivant.
10995  *
10996  * Fork - LGPL
10997  * <script type="text/javascript">
10998  */
10999
11000 /**
11001 /**
11002  * @extends Roo.data.Store
11003  * @class Roo.data.JsonStore
11004  * Small helper class to make creating Stores for JSON data easier. <br/>
11005 <pre><code>
11006 var store = new Roo.data.JsonStore({
11007     url: 'get-images.php',
11008     root: 'images',
11009     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11010 });
11011 </code></pre>
11012  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11013  * JsonReader and HttpProxy (unless inline data is provided).</b>
11014  * @cfg {Array} fields An array of field definition objects, or field name strings.
11015  * @constructor
11016  * @param {Object} config
11017  */
11018 Roo.data.JsonStore = function(c){
11019     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11020         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11021         reader: new Roo.data.JsonReader(c, c.fields)
11022     }));
11023 };
11024 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11025  * Based on:
11026  * Ext JS Library 1.1.1
11027  * Copyright(c) 2006-2007, Ext JS, LLC.
11028  *
11029  * Originally Released Under LGPL - original licence link has changed is not relivant.
11030  *
11031  * Fork - LGPL
11032  * <script type="text/javascript">
11033  */
11034
11035  
11036 Roo.data.Field = function(config){
11037     if(typeof config == "string"){
11038         config = {name: config};
11039     }
11040     Roo.apply(this, config);
11041     
11042     if(!this.type){
11043         this.type = "auto";
11044     }
11045     
11046     var st = Roo.data.SortTypes;
11047     // named sortTypes are supported, here we look them up
11048     if(typeof this.sortType == "string"){
11049         this.sortType = st[this.sortType];
11050     }
11051     
11052     // set default sortType for strings and dates
11053     if(!this.sortType){
11054         switch(this.type){
11055             case "string":
11056                 this.sortType = st.asUCString;
11057                 break;
11058             case "date":
11059                 this.sortType = st.asDate;
11060                 break;
11061             default:
11062                 this.sortType = st.none;
11063         }
11064     }
11065
11066     // define once
11067     var stripRe = /[\$,%]/g;
11068
11069     // prebuilt conversion function for this field, instead of
11070     // switching every time we're reading a value
11071     if(!this.convert){
11072         var cv, dateFormat = this.dateFormat;
11073         switch(this.type){
11074             case "":
11075             case "auto":
11076             case undefined:
11077                 cv = function(v){ return v; };
11078                 break;
11079             case "string":
11080                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11081                 break;
11082             case "int":
11083                 cv = function(v){
11084                     return v !== undefined && v !== null && v !== '' ?
11085                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11086                     };
11087                 break;
11088             case "float":
11089                 cv = function(v){
11090                     return v !== undefined && v !== null && v !== '' ?
11091                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11092                     };
11093                 break;
11094             case "bool":
11095             case "boolean":
11096                 cv = function(v){ return v === true || v === "true" || v == 1; };
11097                 break;
11098             case "date":
11099                 cv = function(v){
11100                     if(!v){
11101                         return '';
11102                     }
11103                     if(v instanceof Date){
11104                         return v;
11105                     }
11106                     if(dateFormat){
11107                         if(dateFormat == "timestamp"){
11108                             return new Date(v*1000);
11109                         }
11110                         return Date.parseDate(v, dateFormat);
11111                     }
11112                     var parsed = Date.parse(v);
11113                     return parsed ? new Date(parsed) : null;
11114                 };
11115              break;
11116             
11117         }
11118         this.convert = cv;
11119     }
11120 };
11121
11122 Roo.data.Field.prototype = {
11123     dateFormat: null,
11124     defaultValue: "",
11125     mapping: null,
11126     sortType : null,
11127     sortDir : "ASC"
11128 };/*
11129  * Based on:
11130  * Ext JS Library 1.1.1
11131  * Copyright(c) 2006-2007, Ext JS, LLC.
11132  *
11133  * Originally Released Under LGPL - original licence link has changed is not relivant.
11134  *
11135  * Fork - LGPL
11136  * <script type="text/javascript">
11137  */
11138  
11139 // Base class for reading structured data from a data source.  This class is intended to be
11140 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11141
11142 /**
11143  * @class Roo.data.DataReader
11144  * Base class for reading structured data from a data source.  This class is intended to be
11145  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11146  */
11147
11148 Roo.data.DataReader = function(meta, recordType){
11149     
11150     this.meta = meta;
11151     
11152     this.recordType = recordType instanceof Array ? 
11153         Roo.data.Record.create(recordType) : recordType;
11154 };
11155
11156 Roo.data.DataReader.prototype = {
11157      /**
11158      * Create an empty record
11159      * @param {Object} data (optional) - overlay some values
11160      * @return {Roo.data.Record} record created.
11161      */
11162     newRow :  function(d) {
11163         var da =  {};
11164         this.recordType.prototype.fields.each(function(c) {
11165             switch( c.type) {
11166                 case 'int' : da[c.name] = 0; break;
11167                 case 'date' : da[c.name] = new Date(); break;
11168                 case 'float' : da[c.name] = 0.0; break;
11169                 case 'boolean' : da[c.name] = false; break;
11170                 default : da[c.name] = ""; break;
11171             }
11172             
11173         });
11174         return new this.recordType(Roo.apply(da, d));
11175     }
11176     
11177 };/*
11178  * Based on:
11179  * Ext JS Library 1.1.1
11180  * Copyright(c) 2006-2007, Ext JS, LLC.
11181  *
11182  * Originally Released Under LGPL - original licence link has changed is not relivant.
11183  *
11184  * Fork - LGPL
11185  * <script type="text/javascript">
11186  */
11187
11188 /**
11189  * @class Roo.data.DataProxy
11190  * @extends Roo.data.Observable
11191  * This class is an abstract base class for implementations which provide retrieval of
11192  * unformatted data objects.<br>
11193  * <p>
11194  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11195  * (of the appropriate type which knows how to parse the data object) to provide a block of
11196  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11197  * <p>
11198  * Custom implementations must implement the load method as described in
11199  * {@link Roo.data.HttpProxy#load}.
11200  */
11201 Roo.data.DataProxy = function(){
11202     this.addEvents({
11203         /**
11204          * @event beforeload
11205          * Fires before a network request is made to retrieve a data object.
11206          * @param {Object} This DataProxy object.
11207          * @param {Object} params The params parameter to the load function.
11208          */
11209         beforeload : true,
11210         /**
11211          * @event load
11212          * Fires before the load method's callback is called.
11213          * @param {Object} This DataProxy object.
11214          * @param {Object} o The data object.
11215          * @param {Object} arg The callback argument object passed to the load function.
11216          */
11217         load : true,
11218         /**
11219          * @event loadexception
11220          * Fires if an Exception occurs during data retrieval.
11221          * @param {Object} This DataProxy object.
11222          * @param {Object} o The data object.
11223          * @param {Object} arg The callback argument object passed to the load function.
11224          * @param {Object} e The Exception.
11225          */
11226         loadexception : true
11227     });
11228     Roo.data.DataProxy.superclass.constructor.call(this);
11229 };
11230
11231 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11232
11233     /**
11234      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11235      */
11236 /*
11237  * Based on:
11238  * Ext JS Library 1.1.1
11239  * Copyright(c) 2006-2007, Ext JS, LLC.
11240  *
11241  * Originally Released Under LGPL - original licence link has changed is not relivant.
11242  *
11243  * Fork - LGPL
11244  * <script type="text/javascript">
11245  */
11246 /**
11247  * @class Roo.data.MemoryProxy
11248  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11249  * to the Reader when its load method is called.
11250  * @constructor
11251  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11252  */
11253 Roo.data.MemoryProxy = function(data){
11254     if (data.data) {
11255         data = data.data;
11256     }
11257     Roo.data.MemoryProxy.superclass.constructor.call(this);
11258     this.data = data;
11259 };
11260
11261 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11262     
11263     /**
11264      * Load data from the requested source (in this case an in-memory
11265      * data object passed to the constructor), read the data object into
11266      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11267      * process that block using the passed callback.
11268      * @param {Object} params This parameter is not used by the MemoryProxy class.
11269      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11270      * object into a block of Roo.data.Records.
11271      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11272      * The function must be passed <ul>
11273      * <li>The Record block object</li>
11274      * <li>The "arg" argument from the load function</li>
11275      * <li>A boolean success indicator</li>
11276      * </ul>
11277      * @param {Object} scope The scope in which to call the callback
11278      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11279      */
11280     load : function(params, reader, callback, scope, arg){
11281         params = params || {};
11282         var result;
11283         try {
11284             result = reader.readRecords(this.data);
11285         }catch(e){
11286             this.fireEvent("loadexception", this, arg, null, e);
11287             callback.call(scope, null, arg, false);
11288             return;
11289         }
11290         callback.call(scope, result, arg, true);
11291     },
11292     
11293     // private
11294     update : function(params, records){
11295         
11296     }
11297 });/*
11298  * Based on:
11299  * Ext JS Library 1.1.1
11300  * Copyright(c) 2006-2007, Ext JS, LLC.
11301  *
11302  * Originally Released Under LGPL - original licence link has changed is not relivant.
11303  *
11304  * Fork - LGPL
11305  * <script type="text/javascript">
11306  */
11307 /**
11308  * @class Roo.data.HttpProxy
11309  * @extends Roo.data.DataProxy
11310  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11311  * configured to reference a certain URL.<br><br>
11312  * <p>
11313  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11314  * from which the running page was served.<br><br>
11315  * <p>
11316  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11317  * <p>
11318  * Be aware that to enable the browser to parse an XML document, the server must set
11319  * the Content-Type header in the HTTP response to "text/xml".
11320  * @constructor
11321  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11322  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11323  * will be used to make the request.
11324  */
11325 Roo.data.HttpProxy = function(conn){
11326     Roo.data.HttpProxy.superclass.constructor.call(this);
11327     // is conn a conn config or a real conn?
11328     this.conn = conn;
11329     this.useAjax = !conn || !conn.events;
11330   
11331 };
11332
11333 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11334     // thse are take from connection...
11335     
11336     /**
11337      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11338      */
11339     /**
11340      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11341      * extra parameters to each request made by this object. (defaults to undefined)
11342      */
11343     /**
11344      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11345      *  to each request made by this object. (defaults to undefined)
11346      */
11347     /**
11348      * @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)
11349      */
11350     /**
11351      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11352      */
11353      /**
11354      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11355      * @type Boolean
11356      */
11357   
11358
11359     /**
11360      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11361      * @type Boolean
11362      */
11363     /**
11364      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11365      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11366      * a finer-grained basis than the DataProxy events.
11367      */
11368     getConnection : function(){
11369         return this.useAjax ? Roo.Ajax : this.conn;
11370     },
11371
11372     /**
11373      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11374      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11375      * process that block using the passed callback.
11376      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11377      * for the request to the remote server.
11378      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11379      * object into a block of Roo.data.Records.
11380      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11381      * The function must be passed <ul>
11382      * <li>The Record block object</li>
11383      * <li>The "arg" argument from the load function</li>
11384      * <li>A boolean success indicator</li>
11385      * </ul>
11386      * @param {Object} scope The scope in which to call the callback
11387      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11388      */
11389     load : function(params, reader, callback, scope, arg){
11390         if(this.fireEvent("beforeload", this, params) !== false){
11391             var  o = {
11392                 params : params || {},
11393                 request: {
11394                     callback : callback,
11395                     scope : scope,
11396                     arg : arg
11397                 },
11398                 reader: reader,
11399                 callback : this.loadResponse,
11400                 scope: this
11401             };
11402             if(this.useAjax){
11403                 Roo.applyIf(o, this.conn);
11404                 if(this.activeRequest){
11405                     Roo.Ajax.abort(this.activeRequest);
11406                 }
11407                 this.activeRequest = Roo.Ajax.request(o);
11408             }else{
11409                 this.conn.request(o);
11410             }
11411         }else{
11412             callback.call(scope||this, null, arg, false);
11413         }
11414     },
11415
11416     // private
11417     loadResponse : function(o, success, response){
11418         delete this.activeRequest;
11419         if(!success){
11420             this.fireEvent("loadexception", this, o, response);
11421             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11422             return;
11423         }
11424         var result;
11425         try {
11426             result = o.reader.read(response);
11427         }catch(e){
11428             this.fireEvent("loadexception", this, o, response, e);
11429             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11430             return;
11431         }
11432         
11433         this.fireEvent("load", this, o, o.request.arg);
11434         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11435     },
11436
11437     // private
11438     update : function(dataSet){
11439
11440     },
11441
11442     // private
11443     updateResponse : function(dataSet){
11444
11445     }
11446 });/*
11447  * Based on:
11448  * Ext JS Library 1.1.1
11449  * Copyright(c) 2006-2007, Ext JS, LLC.
11450  *
11451  * Originally Released Under LGPL - original licence link has changed is not relivant.
11452  *
11453  * Fork - LGPL
11454  * <script type="text/javascript">
11455  */
11456
11457 /**
11458  * @class Roo.data.ScriptTagProxy
11459  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11460  * other than the originating domain of the running page.<br><br>
11461  * <p>
11462  * <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
11463  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11464  * <p>
11465  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11466  * source code that is used as the source inside a &lt;script> tag.<br><br>
11467  * <p>
11468  * In order for the browser to process the returned data, the server must wrap the data object
11469  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11470  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11471  * depending on whether the callback name was passed:
11472  * <p>
11473  * <pre><code>
11474 boolean scriptTag = false;
11475 String cb = request.getParameter("callback");
11476 if (cb != null) {
11477     scriptTag = true;
11478     response.setContentType("text/javascript");
11479 } else {
11480     response.setContentType("application/x-json");
11481 }
11482 Writer out = response.getWriter();
11483 if (scriptTag) {
11484     out.write(cb + "(");
11485 }
11486 out.print(dataBlock.toJsonString());
11487 if (scriptTag) {
11488     out.write(");");
11489 }
11490 </pre></code>
11491  *
11492  * @constructor
11493  * @param {Object} config A configuration object.
11494  */
11495 Roo.data.ScriptTagProxy = function(config){
11496     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11497     Roo.apply(this, config);
11498     this.head = document.getElementsByTagName("head")[0];
11499 };
11500
11501 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11502
11503 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11504     /**
11505      * @cfg {String} url The URL from which to request the data object.
11506      */
11507     /**
11508      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11509      */
11510     timeout : 30000,
11511     /**
11512      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11513      * the server the name of the callback function set up by the load call to process the returned data object.
11514      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11515      * javascript output which calls this named function passing the data object as its only parameter.
11516      */
11517     callbackParam : "callback",
11518     /**
11519      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11520      * name to the request.
11521      */
11522     nocache : true,
11523
11524     /**
11525      * Load data from the configured URL, read the data object into
11526      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11527      * process that block using the passed callback.
11528      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11529      * for the request to the remote server.
11530      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11531      * object into a block of Roo.data.Records.
11532      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11533      * The function must be passed <ul>
11534      * <li>The Record block object</li>
11535      * <li>The "arg" argument from the load function</li>
11536      * <li>A boolean success indicator</li>
11537      * </ul>
11538      * @param {Object} scope The scope in which to call the callback
11539      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11540      */
11541     load : function(params, reader, callback, scope, arg){
11542         if(this.fireEvent("beforeload", this, params) !== false){
11543
11544             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11545
11546             var url = this.url;
11547             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11548             if(this.nocache){
11549                 url += "&_dc=" + (new Date().getTime());
11550             }
11551             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11552             var trans = {
11553                 id : transId,
11554                 cb : "stcCallback"+transId,
11555                 scriptId : "stcScript"+transId,
11556                 params : params,
11557                 arg : arg,
11558                 url : url,
11559                 callback : callback,
11560                 scope : scope,
11561                 reader : reader
11562             };
11563             var conn = this;
11564
11565             window[trans.cb] = function(o){
11566                 conn.handleResponse(o, trans);
11567             };
11568
11569             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11570
11571             if(this.autoAbort !== false){
11572                 this.abort();
11573             }
11574
11575             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11576
11577             var script = document.createElement("script");
11578             script.setAttribute("src", url);
11579             script.setAttribute("type", "text/javascript");
11580             script.setAttribute("id", trans.scriptId);
11581             this.head.appendChild(script);
11582
11583             this.trans = trans;
11584         }else{
11585             callback.call(scope||this, null, arg, false);
11586         }
11587     },
11588
11589     // private
11590     isLoading : function(){
11591         return this.trans ? true : false;
11592     },
11593
11594     /**
11595      * Abort the current server request.
11596      */
11597     abort : function(){
11598         if(this.isLoading()){
11599             this.destroyTrans(this.trans);
11600         }
11601     },
11602
11603     // private
11604     destroyTrans : function(trans, isLoaded){
11605         this.head.removeChild(document.getElementById(trans.scriptId));
11606         clearTimeout(trans.timeoutId);
11607         if(isLoaded){
11608             window[trans.cb] = undefined;
11609             try{
11610                 delete window[trans.cb];
11611             }catch(e){}
11612         }else{
11613             // if hasn't been loaded, wait for load to remove it to prevent script error
11614             window[trans.cb] = function(){
11615                 window[trans.cb] = undefined;
11616                 try{
11617                     delete window[trans.cb];
11618                 }catch(e){}
11619             };
11620         }
11621     },
11622
11623     // private
11624     handleResponse : function(o, trans){
11625         this.trans = false;
11626         this.destroyTrans(trans, true);
11627         var result;
11628         try {
11629             result = trans.reader.readRecords(o);
11630         }catch(e){
11631             this.fireEvent("loadexception", this, o, trans.arg, e);
11632             trans.callback.call(trans.scope||window, null, trans.arg, false);
11633             return;
11634         }
11635         this.fireEvent("load", this, o, trans.arg);
11636         trans.callback.call(trans.scope||window, result, trans.arg, true);
11637     },
11638
11639     // private
11640     handleFailure : function(trans){
11641         this.trans = false;
11642         this.destroyTrans(trans, false);
11643         this.fireEvent("loadexception", this, null, trans.arg);
11644         trans.callback.call(trans.scope||window, null, trans.arg, false);
11645     }
11646 });/*
11647  * Based on:
11648  * Ext JS Library 1.1.1
11649  * Copyright(c) 2006-2007, Ext JS, LLC.
11650  *
11651  * Originally Released Under LGPL - original licence link has changed is not relivant.
11652  *
11653  * Fork - LGPL
11654  * <script type="text/javascript">
11655  */
11656
11657 /**
11658  * @class Roo.data.JsonReader
11659  * @extends Roo.data.DataReader
11660  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11661  * based on mappings in a provided Roo.data.Record constructor.
11662  * 
11663  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11664  * in the reply previously. 
11665  * 
11666  * <p>
11667  * Example code:
11668  * <pre><code>
11669 var RecordDef = Roo.data.Record.create([
11670     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11671     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11672 ]);
11673 var myReader = new Roo.data.JsonReader({
11674     totalProperty: "results",    // The property which contains the total dataset size (optional)
11675     root: "rows",                // The property which contains an Array of row objects
11676     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11677 }, RecordDef);
11678 </code></pre>
11679  * <p>
11680  * This would consume a JSON file like this:
11681  * <pre><code>
11682 { 'results': 2, 'rows': [
11683     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11684     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11685 }
11686 </code></pre>
11687  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11688  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11689  * paged from the remote server.
11690  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11691  * @cfg {String} root name of the property which contains the Array of row objects.
11692  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11693  * @cfg {Array} fields Array of field definition objects
11694  * @constructor
11695  * Create a new JsonReader
11696  * @param {Object} meta Metadata configuration options
11697  * @param {Object} recordType Either an Array of field definition objects,
11698  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11699  */
11700 Roo.data.JsonReader = function(meta, recordType){
11701     
11702     meta = meta || {};
11703     // set some defaults:
11704     Roo.applyIf(meta, {
11705         totalProperty: 'total',
11706         successProperty : 'success',
11707         root : 'data',
11708         id : 'id'
11709     });
11710     
11711     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11712 };
11713 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11714     
11715     /**
11716      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11717      * Used by Store query builder to append _requestMeta to params.
11718      * 
11719      */
11720     metaFromRemote : false,
11721     /**
11722      * This method is only used by a DataProxy which has retrieved data from a remote server.
11723      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11724      * @return {Object} data A data block which is used by an Roo.data.Store object as
11725      * a cache of Roo.data.Records.
11726      */
11727     read : function(response){
11728         var json = response.responseText;
11729        
11730         var o = /* eval:var:o */ eval("("+json+")");
11731         if(!o) {
11732             throw {message: "JsonReader.read: Json object not found"};
11733         }
11734         
11735         if(o.metaData){
11736             
11737             delete this.ef;
11738             this.metaFromRemote = true;
11739             this.meta = o.metaData;
11740             this.recordType = Roo.data.Record.create(o.metaData.fields);
11741             this.onMetaChange(this.meta, this.recordType, o);
11742         }
11743         return this.readRecords(o);
11744     },
11745
11746     // private function a store will implement
11747     onMetaChange : function(meta, recordType, o){
11748
11749     },
11750
11751     /**
11752          * @ignore
11753          */
11754     simpleAccess: function(obj, subsc) {
11755         return obj[subsc];
11756     },
11757
11758         /**
11759          * @ignore
11760          */
11761     getJsonAccessor: function(){
11762         var re = /[\[\.]/;
11763         return function(expr) {
11764             try {
11765                 return(re.test(expr))
11766                     ? new Function("obj", "return obj." + expr)
11767                     : function(obj){
11768                         return obj[expr];
11769                     };
11770             } catch(e){}
11771             return Roo.emptyFn;
11772         };
11773     }(),
11774
11775     /**
11776      * Create a data block containing Roo.data.Records from an XML document.
11777      * @param {Object} o An object which contains an Array of row objects in the property specified
11778      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11779      * which contains the total size of the dataset.
11780      * @return {Object} data A data block which is used by an Roo.data.Store object as
11781      * a cache of Roo.data.Records.
11782      */
11783     readRecords : function(o){
11784         /**
11785          * After any data loads, the raw JSON data is available for further custom processing.
11786          * @type Object
11787          */
11788         this.o = o;
11789         var s = this.meta, Record = this.recordType,
11790             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11791
11792 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11793         if (!this.ef) {
11794             if(s.totalProperty) {
11795                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11796                 }
11797                 if(s.successProperty) {
11798                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11799                 }
11800                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11801                 if (s.id) {
11802                         var g = this.getJsonAccessor(s.id);
11803                         this.getId = function(rec) {
11804                                 var r = g(rec);  
11805                                 return (r === undefined || r === "") ? null : r;
11806                         };
11807                 } else {
11808                         this.getId = function(){return null;};
11809                 }
11810             this.ef = [];
11811             for(var jj = 0; jj < fl; jj++){
11812                 f = fi[jj];
11813                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11814                 this.ef[jj] = this.getJsonAccessor(map);
11815             }
11816         }
11817
11818         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11819         if(s.totalProperty){
11820             var vt = parseInt(this.getTotal(o), 10);
11821             if(!isNaN(vt)){
11822                 totalRecords = vt;
11823             }
11824         }
11825         if(s.successProperty){
11826             var vs = this.getSuccess(o);
11827             if(vs === false || vs === 'false'){
11828                 success = false;
11829             }
11830         }
11831         var records = [];
11832         for(var i = 0; i < c; i++){
11833                 var n = root[i];
11834             var values = {};
11835             var id = this.getId(n);
11836             for(var j = 0; j < fl; j++){
11837                 f = fi[j];
11838             var v = this.ef[j](n);
11839             if (!f.convert) {
11840                 Roo.log('missing convert for ' + f.name);
11841                 Roo.log(f);
11842                 continue;
11843             }
11844             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11845             }
11846             var record = new Record(values, id);
11847             record.json = n;
11848             records[i] = record;
11849         }
11850         return {
11851             raw : o,
11852             success : success,
11853             records : records,
11854             totalRecords : totalRecords
11855         };
11856     }
11857 });/*
11858  * Based on:
11859  * Ext JS Library 1.1.1
11860  * Copyright(c) 2006-2007, Ext JS, LLC.
11861  *
11862  * Originally Released Under LGPL - original licence link has changed is not relivant.
11863  *
11864  * Fork - LGPL
11865  * <script type="text/javascript">
11866  */
11867
11868 /**
11869  * @class Roo.data.ArrayReader
11870  * @extends Roo.data.DataReader
11871  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11872  * Each element of that Array represents a row of data fields. The
11873  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11874  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11875  * <p>
11876  * Example code:.
11877  * <pre><code>
11878 var RecordDef = Roo.data.Record.create([
11879     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11880     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11881 ]);
11882 var myReader = new Roo.data.ArrayReader({
11883     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11884 }, RecordDef);
11885 </code></pre>
11886  * <p>
11887  * This would consume an Array like this:
11888  * <pre><code>
11889 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11890   </code></pre>
11891  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11892  * @constructor
11893  * Create a new JsonReader
11894  * @param {Object} meta Metadata configuration options.
11895  * @param {Object} recordType Either an Array of field definition objects
11896  * as specified to {@link Roo.data.Record#create},
11897  * or an {@link Roo.data.Record} object
11898  * created using {@link Roo.data.Record#create}.
11899  */
11900 Roo.data.ArrayReader = function(meta, recordType){
11901     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11902 };
11903
11904 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11905     /**
11906      * Create a data block containing Roo.data.Records from an XML document.
11907      * @param {Object} o An Array of row objects which represents the dataset.
11908      * @return {Object} data A data block which is used by an Roo.data.Store object as
11909      * a cache of Roo.data.Records.
11910      */
11911     readRecords : function(o){
11912         var sid = this.meta ? this.meta.id : null;
11913         var recordType = this.recordType, fields = recordType.prototype.fields;
11914         var records = [];
11915         var root = o;
11916             for(var i = 0; i < root.length; i++){
11917                     var n = root[i];
11918                 var values = {};
11919                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11920                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11921                 var f = fields.items[j];
11922                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11923                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11924                 v = f.convert(v);
11925                 values[f.name] = v;
11926             }
11927                 var record = new recordType(values, id);
11928                 record.json = n;
11929                 records[records.length] = record;
11930             }
11931             return {
11932                 records : records,
11933                 totalRecords : records.length
11934             };
11935     }
11936 });/*
11937  * - LGPL
11938  * * 
11939  */
11940
11941 /**
11942  * @class Roo.bootstrap.ComboBox
11943  * @extends Roo.bootstrap.TriggerField
11944  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11945  * @cfg {Boolean} append (true|false) default false
11946  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11947  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11948  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11949  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11950  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11951  * @cfg {Boolean} animate default true
11952  * @cfg {Boolean} emptyResultText only for touch device
11953  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11954  * @constructor
11955  * Create a new ComboBox.
11956  * @param {Object} config Configuration options
11957  */
11958 Roo.bootstrap.ComboBox = function(config){
11959     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11960     this.addEvents({
11961         /**
11962          * @event expand
11963          * Fires when the dropdown list is expanded
11964              * @param {Roo.bootstrap.ComboBox} combo This combo box
11965              */
11966         'expand' : true,
11967         /**
11968          * @event collapse
11969          * Fires when the dropdown list is collapsed
11970              * @param {Roo.bootstrap.ComboBox} combo This combo box
11971              */
11972         'collapse' : true,
11973         /**
11974          * @event beforeselect
11975          * Fires before a list item is selected. Return false to cancel the selection.
11976              * @param {Roo.bootstrap.ComboBox} combo This combo box
11977              * @param {Roo.data.Record} record The data record returned from the underlying store
11978              * @param {Number} index The index of the selected item in the dropdown list
11979              */
11980         'beforeselect' : true,
11981         /**
11982          * @event select
11983          * Fires when a list item is selected
11984              * @param {Roo.bootstrap.ComboBox} combo This combo box
11985              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11986              * @param {Number} index The index of the selected item in the dropdown list
11987              */
11988         'select' : true,
11989         /**
11990          * @event beforequery
11991          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11992          * The event object passed has these properties:
11993              * @param {Roo.bootstrap.ComboBox} combo This combo box
11994              * @param {String} query The query
11995              * @param {Boolean} forceAll true to force "all" query
11996              * @param {Boolean} cancel true to cancel the query
11997              * @param {Object} e The query event object
11998              */
11999         'beforequery': true,
12000          /**
12001          * @event add
12002          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12003              * @param {Roo.bootstrap.ComboBox} combo This combo box
12004              */
12005         'add' : true,
12006         /**
12007          * @event edit
12008          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12009              * @param {Roo.bootstrap.ComboBox} combo This combo box
12010              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12011              */
12012         'edit' : true,
12013         /**
12014          * @event remove
12015          * Fires when the remove value from the combobox array
12016              * @param {Roo.bootstrap.ComboBox} combo This combo box
12017              */
12018         'remove' : true,
12019         /**
12020          * @event afterremove
12021          * Fires when the remove value from the combobox array
12022              * @param {Roo.bootstrap.ComboBox} combo This combo box
12023              */
12024         'afterremove' : true,
12025         /**
12026          * @event specialfilter
12027          * Fires when specialfilter
12028             * @param {Roo.bootstrap.ComboBox} combo This combo box
12029             */
12030         'specialfilter' : true,
12031         /**
12032          * @event tick
12033          * Fires when tick the element
12034             * @param {Roo.bootstrap.ComboBox} combo This combo box
12035             */
12036         'tick' : true,
12037         /**
12038          * @event touchviewdisplay
12039          * Fires when touch view require special display (default is using displayField)
12040             * @param {Roo.bootstrap.ComboBox} combo This combo box
12041             * @param {Object} cfg set html .
12042             */
12043         'touchviewdisplay' : true
12044         
12045     });
12046     
12047     this.item = [];
12048     this.tickItems = [];
12049     
12050     this.selectedIndex = -1;
12051     if(this.mode == 'local'){
12052         if(config.queryDelay === undefined){
12053             this.queryDelay = 10;
12054         }
12055         if(config.minChars === undefined){
12056             this.minChars = 0;
12057         }
12058     }
12059 };
12060
12061 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12062      
12063     /**
12064      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12065      * rendering into an Roo.Editor, defaults to false)
12066      */
12067     /**
12068      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12069      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12070      */
12071     /**
12072      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12073      */
12074     /**
12075      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12076      * the dropdown list (defaults to undefined, with no header element)
12077      */
12078
12079      /**
12080      * @cfg {String/Roo.Template} tpl The template to use to render the output
12081      */
12082      
12083      /**
12084      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12085      */
12086     listWidth: undefined,
12087     /**
12088      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12089      * mode = 'remote' or 'text' if mode = 'local')
12090      */
12091     displayField: undefined,
12092     
12093     /**
12094      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12095      * mode = 'remote' or 'value' if mode = 'local'). 
12096      * Note: use of a valueField requires the user make a selection
12097      * in order for a value to be mapped.
12098      */
12099     valueField: undefined,
12100     /**
12101      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12102      */
12103     modalTitle : '',
12104     
12105     /**
12106      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12107      * field's data value (defaults to the underlying DOM element's name)
12108      */
12109     hiddenName: undefined,
12110     /**
12111      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12112      */
12113     listClass: '',
12114     /**
12115      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12116      */
12117     selectedClass: 'active',
12118     
12119     /**
12120      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12121      */
12122     shadow:'sides',
12123     /**
12124      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12125      * anchor positions (defaults to 'tl-bl')
12126      */
12127     listAlign: 'tl-bl?',
12128     /**
12129      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12130      */
12131     maxHeight: 300,
12132     /**
12133      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12134      * query specified by the allQuery config option (defaults to 'query')
12135      */
12136     triggerAction: 'query',
12137     /**
12138      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12139      * (defaults to 4, does not apply if editable = false)
12140      */
12141     minChars : 4,
12142     /**
12143      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12144      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12145      */
12146     typeAhead: false,
12147     /**
12148      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12149      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12150      */
12151     queryDelay: 500,
12152     /**
12153      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12154      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12155      */
12156     pageSize: 0,
12157     /**
12158      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12159      * when editable = true (defaults to false)
12160      */
12161     selectOnFocus:false,
12162     /**
12163      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12164      */
12165     queryParam: 'query',
12166     /**
12167      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12168      * when mode = 'remote' (defaults to 'Loading...')
12169      */
12170     loadingText: 'Loading...',
12171     /**
12172      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12173      */
12174     resizable: false,
12175     /**
12176      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12177      */
12178     handleHeight : 8,
12179     /**
12180      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12181      * traditional select (defaults to true)
12182      */
12183     editable: true,
12184     /**
12185      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12186      */
12187     allQuery: '',
12188     /**
12189      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12190      */
12191     mode: 'remote',
12192     /**
12193      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12194      * listWidth has a higher value)
12195      */
12196     minListWidth : 70,
12197     /**
12198      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12199      * allow the user to set arbitrary text into the field (defaults to false)
12200      */
12201     forceSelection:false,
12202     /**
12203      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12204      * if typeAhead = true (defaults to 250)
12205      */
12206     typeAheadDelay : 250,
12207     /**
12208      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12209      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12210      */
12211     valueNotFoundText : undefined,
12212     /**
12213      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12214      */
12215     blockFocus : false,
12216     
12217     /**
12218      * @cfg {Boolean} disableClear Disable showing of clear button.
12219      */
12220     disableClear : false,
12221     /**
12222      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12223      */
12224     alwaysQuery : false,
12225     
12226     /**
12227      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12228      */
12229     multiple : false,
12230     
12231     /**
12232      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12233      */
12234     invalidClass : "has-warning",
12235     
12236     /**
12237      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12238      */
12239     validClass : "has-success",
12240     
12241     /**
12242      * @cfg {Boolean} specialFilter (true|false) special filter default false
12243      */
12244     specialFilter : false,
12245     
12246     /**
12247      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12248      */
12249     mobileTouchView : true,
12250     
12251     /**
12252      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12253      */
12254     useNativeIOS : false,
12255     
12256     ios_options : false,
12257     
12258     //private
12259     addicon : false,
12260     editicon: false,
12261     
12262     page: 0,
12263     hasQuery: false,
12264     append: false,
12265     loadNext: false,
12266     autoFocus : true,
12267     tickable : false,
12268     btnPosition : 'right',
12269     triggerList : true,
12270     showToggleBtn : true,
12271     animate : true,
12272     emptyResultText: 'Empty',
12273     triggerText : 'Select',
12274     
12275     // element that contains real text value.. (when hidden is used..)
12276     
12277     getAutoCreate : function()
12278     {
12279         var cfg = false;
12280         
12281         /*
12282          * Render classic select for iso
12283          */
12284         
12285         if(Roo.isIOS && this.useNativeIOS){
12286             cfg = this.getAutoCreateNativeIOS();
12287             return cfg;
12288         }
12289         
12290         /*
12291          * Touch Devices
12292          */
12293         
12294         if(Roo.isTouch && this.mobileTouchView){
12295             cfg = this.getAutoCreateTouchView();
12296             return cfg;;
12297         }
12298         
12299         /*
12300          *  Normal ComboBox
12301          */
12302         if(!this.tickable){
12303             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12304             return cfg;
12305         }
12306         
12307         /*
12308          *  ComboBox with tickable selections
12309          */
12310              
12311         var align = this.labelAlign || this.parentLabelAlign();
12312         
12313         cfg = {
12314             cls : 'form-group roo-combobox-tickable' //input-group
12315         };
12316         
12317         var buttons = {
12318             tag : 'div',
12319             cls : 'tickable-buttons',
12320             cn : [
12321                 {
12322                     tag : 'button',
12323                     type : 'button',
12324                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12325                     html : this.triggerText
12326                 },
12327                 {
12328                     tag : 'button',
12329                     type : 'button',
12330                     name : 'ok',
12331                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12332                     html : 'Done'
12333                 },
12334                 {
12335                     tag : 'button',
12336                     type : 'button',
12337                     name : 'cancel',
12338                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12339                     html : 'Cancel'
12340                 }
12341             ]
12342         };
12343         
12344         if(this.editable){
12345             buttons.cn.unshift({
12346                 tag: 'input',
12347                 cls: 'roo-select2-search-field-input'
12348             });
12349         }
12350         
12351         var _this = this;
12352         
12353         Roo.each(buttons.cn, function(c){
12354             if (_this.size) {
12355                 c.cls += ' btn-' + _this.size;
12356             }
12357
12358             if (_this.disabled) {
12359                 c.disabled = true;
12360             }
12361         });
12362         
12363         var box = {
12364             tag: 'div',
12365             cn: [
12366                 {
12367                     tag: 'input',
12368                     type : 'hidden',
12369                     cls: 'form-hidden-field'
12370                 },
12371                 {
12372                     tag: 'ul',
12373                     cls: 'roo-select2-choices',
12374                     cn:[
12375                         {
12376                             tag: 'li',
12377                             cls: 'roo-select2-search-field',
12378                             cn: [
12379
12380                                 buttons
12381                             ]
12382                         }
12383                     ]
12384                 }
12385             ]
12386         };
12387         
12388         var combobox = {
12389             cls: 'roo-select2-container input-group roo-select2-container-multi',
12390             cn: [
12391                 box
12392 //                {
12393 //                    tag: 'ul',
12394 //                    cls: 'typeahead typeahead-long dropdown-menu',
12395 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12396 //                }
12397             ]
12398         };
12399         
12400         if(this.hasFeedback && !this.allowBlank){
12401             
12402             var feedback = {
12403                 tag: 'span',
12404                 cls: 'glyphicon form-control-feedback'
12405             };
12406
12407             combobox.cn.push(feedback);
12408         }
12409         
12410         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12411             
12412 //                Roo.log("left and has label");
12413             cfg.cn = [
12414                 {
12415                     tag : 'i',
12416                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12417                     tooltip : 'This field is required'
12418                 },
12419                 {
12420                     tag: 'label',
12421                     'for' :  id,
12422                     cls : 'control-label col-sm-' + this.labelWidth,
12423                     html : this.fieldLabel
12424
12425                 },
12426                 {
12427                     cls : "col-sm-" + (12 - this.labelWidth), 
12428                     cn: [
12429                         combobox
12430                     ]
12431                 }
12432
12433             ];
12434
12435             if(this.indicatorpos == 'right'){
12436                 
12437                 cfg.cn = [
12438                     {
12439                         tag: 'label',
12440                         'for' :  id,
12441                         cls : 'control-label col-sm-' + this.labelWidth,
12442                         html : this.fieldLabel
12443
12444                     },
12445                     {
12446                         tag : 'i',
12447                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12448                         tooltip : 'This field is required'
12449                     },
12450                     {
12451                         cls : "col-sm-" + (12 - this.labelWidth), 
12452                         cn: [
12453                             combobox
12454                         ]
12455                     }
12456
12457                 ];
12458             
12459             }
12460                 
12461                 
12462         } else if ( this.fieldLabel.length) {
12463 //                Roo.log(" label");
12464                  cfg.cn = [
12465                     {
12466                         tag : 'i',
12467                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12468                         tooltip : 'This field is required'
12469                     },
12470                     {
12471                         tag: 'label',
12472                         //cls : 'input-group-addon',
12473                         html : this.fieldLabel
12474                         
12475                     },
12476                     
12477                     combobox
12478                     
12479                 ];
12480                 
12481                 if(this.indicatorpos == 'right'){
12482                     
12483                     cfg.cn = [
12484                         {
12485                             tag: 'label',
12486                             //cls : 'input-group-addon',
12487                             html : this.fieldLabel
12488
12489                         },
12490                         
12491                         {
12492                             tag : 'i',
12493                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12494                             tooltip : 'This field is required'
12495                         },
12496                         
12497                         combobox
12498
12499                     ];
12500                 
12501                 }
12502
12503         } else {
12504             
12505 //                Roo.log(" no label && no align");
12506                 cfg = combobox
12507                      
12508                 
12509         }
12510          
12511         var settings=this;
12512         ['xs','sm','md','lg'].map(function(size){
12513             if (settings[size]) {
12514                 cfg.cls += ' col-' + size + '-' + settings[size];
12515             }
12516         });
12517         
12518         return cfg;
12519         
12520     },
12521     
12522     _initEventsCalled : false,
12523     
12524     // private
12525     initEvents: function()
12526     {   
12527         if (this._initEventsCalled) { // as we call render... prevent looping...
12528             return;
12529         }
12530         this._initEventsCalled = true;
12531         
12532         if (!this.store) {
12533             throw "can not find store for combo";
12534         }
12535         
12536         this.store = Roo.factory(this.store, Roo.data);
12537         
12538         // if we are building from html. then this element is so complex, that we can not really
12539         // use the rendered HTML.
12540         // so we have to trash and replace the previous code.
12541         if (Roo.XComponent.build_from_html) {
12542             
12543             // remove this element....
12544             var e = this.el.dom, k=0;
12545             while (e ) { e = e.previousSibling;  ++k;}
12546
12547             this.el.remove();
12548             
12549             this.el=false;
12550             this.rendered = false;
12551             
12552             this.render(this.parent().getChildContainer(true), k);
12553             
12554             
12555             
12556         }
12557         
12558         if(Roo.isIOS && this.useNativeIOS){
12559             this.initIOSView();
12560             return;
12561         }
12562         
12563         /*
12564          * Touch Devices
12565          */
12566         
12567         if(Roo.isTouch && this.mobileTouchView){
12568             this.initTouchView();
12569             return;
12570         }
12571         
12572         if(this.tickable){
12573             this.initTickableEvents();
12574             return;
12575         }
12576         
12577         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12578         
12579         if(this.hiddenName){
12580             
12581             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12582             
12583             this.hiddenField.dom.value =
12584                 this.hiddenValue !== undefined ? this.hiddenValue :
12585                 this.value !== undefined ? this.value : '';
12586
12587             // prevent input submission
12588             this.el.dom.removeAttribute('name');
12589             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12590              
12591              
12592         }
12593         //if(Roo.isGecko){
12594         //    this.el.dom.setAttribute('autocomplete', 'off');
12595         //}
12596         
12597         var cls = 'x-combo-list';
12598         
12599         //this.list = new Roo.Layer({
12600         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12601         //});
12602         
12603         var _this = this;
12604         
12605         (function(){
12606             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12607             _this.list.setWidth(lw);
12608         }).defer(100);
12609         
12610         this.list.on('mouseover', this.onViewOver, this);
12611         this.list.on('mousemove', this.onViewMove, this);
12612         
12613         this.list.on('scroll', this.onViewScroll, this);
12614         
12615         /*
12616         this.list.swallowEvent('mousewheel');
12617         this.assetHeight = 0;
12618
12619         if(this.title){
12620             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12621             this.assetHeight += this.header.getHeight();
12622         }
12623
12624         this.innerList = this.list.createChild({cls:cls+'-inner'});
12625         this.innerList.on('mouseover', this.onViewOver, this);
12626         this.innerList.on('mousemove', this.onViewMove, this);
12627         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12628         
12629         if(this.allowBlank && !this.pageSize && !this.disableClear){
12630             this.footer = this.list.createChild({cls:cls+'-ft'});
12631             this.pageTb = new Roo.Toolbar(this.footer);
12632            
12633         }
12634         if(this.pageSize){
12635             this.footer = this.list.createChild({cls:cls+'-ft'});
12636             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12637                     {pageSize: this.pageSize});
12638             
12639         }
12640         
12641         if (this.pageTb && this.allowBlank && !this.disableClear) {
12642             var _this = this;
12643             this.pageTb.add(new Roo.Toolbar.Fill(), {
12644                 cls: 'x-btn-icon x-btn-clear',
12645                 text: '&#160;',
12646                 handler: function()
12647                 {
12648                     _this.collapse();
12649                     _this.clearValue();
12650                     _this.onSelect(false, -1);
12651                 }
12652             });
12653         }
12654         if (this.footer) {
12655             this.assetHeight += this.footer.getHeight();
12656         }
12657         */
12658             
12659         if(!this.tpl){
12660             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12661         }
12662
12663         this.view = new Roo.View(this.list, this.tpl, {
12664             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12665         });
12666         //this.view.wrapEl.setDisplayed(false);
12667         this.view.on('click', this.onViewClick, this);
12668         
12669         
12670         
12671         this.store.on('beforeload', this.onBeforeLoad, this);
12672         this.store.on('load', this.onLoad, this);
12673         this.store.on('loadexception', this.onLoadException, this);
12674         /*
12675         if(this.resizable){
12676             this.resizer = new Roo.Resizable(this.list,  {
12677                pinned:true, handles:'se'
12678             });
12679             this.resizer.on('resize', function(r, w, h){
12680                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12681                 this.listWidth = w;
12682                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12683                 this.restrictHeight();
12684             }, this);
12685             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12686         }
12687         */
12688         if(!this.editable){
12689             this.editable = true;
12690             this.setEditable(false);
12691         }
12692         
12693         /*
12694         
12695         if (typeof(this.events.add.listeners) != 'undefined') {
12696             
12697             this.addicon = this.wrap.createChild(
12698                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12699        
12700             this.addicon.on('click', function(e) {
12701                 this.fireEvent('add', this);
12702             }, this);
12703         }
12704         if (typeof(this.events.edit.listeners) != 'undefined') {
12705             
12706             this.editicon = this.wrap.createChild(
12707                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12708             if (this.addicon) {
12709                 this.editicon.setStyle('margin-left', '40px');
12710             }
12711             this.editicon.on('click', function(e) {
12712                 
12713                 // we fire even  if inothing is selected..
12714                 this.fireEvent('edit', this, this.lastData );
12715                 
12716             }, this);
12717         }
12718         */
12719         
12720         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12721             "up" : function(e){
12722                 this.inKeyMode = true;
12723                 this.selectPrev();
12724             },
12725
12726             "down" : function(e){
12727                 if(!this.isExpanded()){
12728                     this.onTriggerClick();
12729                 }else{
12730                     this.inKeyMode = true;
12731                     this.selectNext();
12732                 }
12733             },
12734
12735             "enter" : function(e){
12736 //                this.onViewClick();
12737                 //return true;
12738                 this.collapse();
12739                 
12740                 if(this.fireEvent("specialkey", this, e)){
12741                     this.onViewClick(false);
12742                 }
12743                 
12744                 return true;
12745             },
12746
12747             "esc" : function(e){
12748                 this.collapse();
12749             },
12750
12751             "tab" : function(e){
12752                 this.collapse();
12753                 
12754                 if(this.fireEvent("specialkey", this, e)){
12755                     this.onViewClick(false);
12756                 }
12757                 
12758                 return true;
12759             },
12760
12761             scope : this,
12762
12763             doRelay : function(foo, bar, hname){
12764                 if(hname == 'down' || this.scope.isExpanded()){
12765                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12766                 }
12767                 return true;
12768             },
12769
12770             forceKeyDown: true
12771         });
12772         
12773         
12774         this.queryDelay = Math.max(this.queryDelay || 10,
12775                 this.mode == 'local' ? 10 : 250);
12776         
12777         
12778         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12779         
12780         if(this.typeAhead){
12781             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12782         }
12783         if(this.editable !== false){
12784             this.inputEl().on("keyup", this.onKeyUp, this);
12785         }
12786         if(this.forceSelection){
12787             this.inputEl().on('blur', this.doForce, this);
12788         }
12789         
12790         if(this.multiple){
12791             this.choices = this.el.select('ul.roo-select2-choices', true).first();
12792             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12793         }
12794     },
12795     
12796     initTickableEvents: function()
12797     {   
12798         this.createList();
12799         
12800         if(this.hiddenName){
12801             
12802             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12803             
12804             this.hiddenField.dom.value =
12805                 this.hiddenValue !== undefined ? this.hiddenValue :
12806                 this.value !== undefined ? this.value : '';
12807
12808             // prevent input submission
12809             this.el.dom.removeAttribute('name');
12810             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12811              
12812              
12813         }
12814         
12815 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12816         
12817         this.choices = this.el.select('ul.roo-select2-choices', true).first();
12818         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12819         if(this.triggerList){
12820             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12821         }
12822          
12823         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12824         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12825         
12826         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12827         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12828         
12829         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12830         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12831         
12832         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12833         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12834         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12835         
12836         this.okBtn.hide();
12837         this.cancelBtn.hide();
12838         
12839         var _this = this;
12840         
12841         (function(){
12842             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12843             _this.list.setWidth(lw);
12844         }).defer(100);
12845         
12846         this.list.on('mouseover', this.onViewOver, this);
12847         this.list.on('mousemove', this.onViewMove, this);
12848         
12849         this.list.on('scroll', this.onViewScroll, this);
12850         
12851         if(!this.tpl){
12852             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></li>';
12853         }
12854
12855         this.view = new Roo.View(this.list, this.tpl, {
12856             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12857         });
12858         
12859         //this.view.wrapEl.setDisplayed(false);
12860         this.view.on('click', this.onViewClick, this);
12861         
12862         
12863         
12864         this.store.on('beforeload', this.onBeforeLoad, this);
12865         this.store.on('load', this.onLoad, this);
12866         this.store.on('loadexception', this.onLoadException, this);
12867         
12868         if(this.editable){
12869             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12870                 "up" : function(e){
12871                     this.inKeyMode = true;
12872                     this.selectPrev();
12873                 },
12874
12875                 "down" : function(e){
12876                     this.inKeyMode = true;
12877                     this.selectNext();
12878                 },
12879
12880                 "enter" : function(e){
12881                     if(this.fireEvent("specialkey", this, e)){
12882                         this.onViewClick(false);
12883                     }
12884                     
12885                     return true;
12886                 },
12887
12888                 "esc" : function(e){
12889                     this.onTickableFooterButtonClick(e, false, false);
12890                 },
12891
12892                 "tab" : function(e){
12893                     this.fireEvent("specialkey", this, e);
12894                     
12895                     this.onTickableFooterButtonClick(e, false, false);
12896                     
12897                     return true;
12898                 },
12899
12900                 scope : this,
12901
12902                 doRelay : function(e, fn, key){
12903                     if(this.scope.isExpanded()){
12904                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12905                     }
12906                     return true;
12907                 },
12908
12909                 forceKeyDown: true
12910             });
12911         }
12912         
12913         this.queryDelay = Math.max(this.queryDelay || 10,
12914                 this.mode == 'local' ? 10 : 250);
12915         
12916         
12917         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12918         
12919         if(this.typeAhead){
12920             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12921         }
12922         
12923         if(this.editable !== false){
12924             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12925         }
12926         
12927     },
12928
12929     onDestroy : function(){
12930         if(this.view){
12931             this.view.setStore(null);
12932             this.view.el.removeAllListeners();
12933             this.view.el.remove();
12934             this.view.purgeListeners();
12935         }
12936         if(this.list){
12937             this.list.dom.innerHTML  = '';
12938         }
12939         
12940         if(this.store){
12941             this.store.un('beforeload', this.onBeforeLoad, this);
12942             this.store.un('load', this.onLoad, this);
12943             this.store.un('loadexception', this.onLoadException, this);
12944         }
12945         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12946     },
12947
12948     // private
12949     fireKey : function(e){
12950         if(e.isNavKeyPress() && !this.list.isVisible()){
12951             this.fireEvent("specialkey", this, e);
12952         }
12953     },
12954
12955     // private
12956     onResize: function(w, h){
12957 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12958 //        
12959 //        if(typeof w != 'number'){
12960 //            // we do not handle it!?!?
12961 //            return;
12962 //        }
12963 //        var tw = this.trigger.getWidth();
12964 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12965 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12966 //        var x = w - tw;
12967 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12968 //            
12969 //        //this.trigger.setStyle('left', x+'px');
12970 //        
12971 //        if(this.list && this.listWidth === undefined){
12972 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12973 //            this.list.setWidth(lw);
12974 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12975 //        }
12976         
12977     
12978         
12979     },
12980
12981     /**
12982      * Allow or prevent the user from directly editing the field text.  If false is passed,
12983      * the user will only be able to select from the items defined in the dropdown list.  This method
12984      * is the runtime equivalent of setting the 'editable' config option at config time.
12985      * @param {Boolean} value True to allow the user to directly edit the field text
12986      */
12987     setEditable : function(value){
12988         if(value == this.editable){
12989             return;
12990         }
12991         this.editable = value;
12992         if(!value){
12993             this.inputEl().dom.setAttribute('readOnly', true);
12994             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12995             this.inputEl().addClass('x-combo-noedit');
12996         }else{
12997             this.inputEl().dom.setAttribute('readOnly', false);
12998             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12999             this.inputEl().removeClass('x-combo-noedit');
13000         }
13001     },
13002
13003     // private
13004     
13005     onBeforeLoad : function(combo,opts){
13006         if(!this.hasFocus){
13007             return;
13008         }
13009          if (!opts.add) {
13010             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13011          }
13012         this.restrictHeight();
13013         this.selectedIndex = -1;
13014     },
13015
13016     // private
13017     onLoad : function(){
13018         
13019         this.hasQuery = false;
13020         
13021         if(!this.hasFocus){
13022             return;
13023         }
13024         
13025         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13026             this.loading.hide();
13027         }
13028              
13029         if(this.store.getCount() > 0){
13030             this.expand();
13031             this.restrictHeight();
13032             if(this.lastQuery == this.allQuery){
13033                 if(this.editable && !this.tickable){
13034                     this.inputEl().dom.select();
13035                 }
13036                 
13037                 if(
13038                     !this.selectByValue(this.value, true) &&
13039                     this.autoFocus && 
13040                     (
13041                         !this.store.lastOptions ||
13042                         typeof(this.store.lastOptions.add) == 'undefined' || 
13043                         this.store.lastOptions.add != true
13044                     )
13045                 ){
13046                     this.select(0, true);
13047                 }
13048             }else{
13049                 if(this.autoFocus){
13050                     this.selectNext();
13051                 }
13052                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13053                     this.taTask.delay(this.typeAheadDelay);
13054                 }
13055             }
13056         }else{
13057             this.onEmptyResults();
13058         }
13059         
13060         //this.el.focus();
13061     },
13062     // private
13063     onLoadException : function()
13064     {
13065         this.hasQuery = false;
13066         
13067         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13068             this.loading.hide();
13069         }
13070         
13071         if(this.tickable && this.editable){
13072             return;
13073         }
13074         
13075         this.collapse();
13076         // only causes errors at present
13077         //Roo.log(this.store.reader.jsonData);
13078         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13079             // fixme
13080             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13081         //}
13082         
13083         
13084     },
13085     // private
13086     onTypeAhead : function(){
13087         if(this.store.getCount() > 0){
13088             var r = this.store.getAt(0);
13089             var newValue = r.data[this.displayField];
13090             var len = newValue.length;
13091             var selStart = this.getRawValue().length;
13092             
13093             if(selStart != len){
13094                 this.setRawValue(newValue);
13095                 this.selectText(selStart, newValue.length);
13096             }
13097         }
13098     },
13099
13100     // private
13101     onSelect : function(record, index){
13102         
13103         if(this.fireEvent('beforeselect', this, record, index) !== false){
13104         
13105             this.setFromData(index > -1 ? record.data : false);
13106             
13107             this.collapse();
13108             this.fireEvent('select', this, record, index);
13109         }
13110     },
13111
13112     /**
13113      * Returns the currently selected field value or empty string if no value is set.
13114      * @return {String} value The selected value
13115      */
13116     getValue : function()
13117     {
13118         if(Roo.isIOS && this.useNativeIOS){
13119             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13120         }
13121         
13122         if(this.multiple){
13123             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13124         }
13125         
13126         if(this.valueField){
13127             return typeof this.value != 'undefined' ? this.value : '';
13128         }else{
13129             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13130         }
13131     },
13132     
13133     getRawValue : function()
13134     {
13135         if(Roo.isIOS && this.useNativeIOS){
13136             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13137         }
13138         
13139         var v = this.inputEl().getValue();
13140         
13141         return v;
13142     },
13143
13144     /**
13145      * Clears any text/value currently set in the field
13146      */
13147     clearValue : function(){
13148         
13149         if(this.hiddenField){
13150             this.hiddenField.dom.value = '';
13151         }
13152         this.value = '';
13153         this.setRawValue('');
13154         this.lastSelectionText = '';
13155         this.lastData = false;
13156         
13157         var close = this.closeTriggerEl();
13158         
13159         if(close){
13160             close.hide();
13161         }
13162         
13163         this.validate();
13164         
13165     },
13166
13167     /**
13168      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13169      * will be displayed in the field.  If the value does not match the data value of an existing item,
13170      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13171      * Otherwise the field will be blank (although the value will still be set).
13172      * @param {String} value The value to match
13173      */
13174     setValue : function(v)
13175     {
13176         if(Roo.isIOS && this.useNativeIOS){
13177             this.setIOSValue(v);
13178             return;
13179         }
13180         
13181         if(this.multiple){
13182             this.syncValue();
13183             return;
13184         }
13185         
13186         var text = v;
13187         if(this.valueField){
13188             var r = this.findRecord(this.valueField, v);
13189             if(r){
13190                 text = r.data[this.displayField];
13191             }else if(this.valueNotFoundText !== undefined){
13192                 text = this.valueNotFoundText;
13193             }
13194         }
13195         this.lastSelectionText = text;
13196         if(this.hiddenField){
13197             this.hiddenField.dom.value = v;
13198         }
13199         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13200         this.value = v;
13201         
13202         var close = this.closeTriggerEl();
13203         
13204         if(close){
13205             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13206         }
13207         
13208         this.validate();
13209     },
13210     /**
13211      * @property {Object} the last set data for the element
13212      */
13213     
13214     lastData : false,
13215     /**
13216      * Sets the value of the field based on a object which is related to the record format for the store.
13217      * @param {Object} value the value to set as. or false on reset?
13218      */
13219     setFromData : function(o){
13220         
13221         if(this.multiple){
13222             this.addItem(o);
13223             return;
13224         }
13225             
13226         var dv = ''; // display value
13227         var vv = ''; // value value..
13228         this.lastData = o;
13229         if (this.displayField) {
13230             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13231         } else {
13232             // this is an error condition!!!
13233             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13234         }
13235         
13236         if(this.valueField){
13237             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13238         }
13239         
13240         var close = this.closeTriggerEl();
13241         
13242         if(close){
13243             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13244         }
13245         
13246         if(this.hiddenField){
13247             this.hiddenField.dom.value = vv;
13248             
13249             this.lastSelectionText = dv;
13250             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13251             this.value = vv;
13252             return;
13253         }
13254         // no hidden field.. - we store the value in 'value', but still display
13255         // display field!!!!
13256         this.lastSelectionText = dv;
13257         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13258         this.value = vv;
13259         
13260         
13261         
13262     },
13263     // private
13264     reset : function(){
13265         // overridden so that last data is reset..
13266         
13267         if(this.multiple){
13268             this.clearItem();
13269             return;
13270         }
13271         
13272         this.setValue(this.originalValue);
13273         //this.clearInvalid();
13274         this.lastData = false;
13275         if (this.view) {
13276             this.view.clearSelections();
13277         }
13278         
13279         this.validate();
13280     },
13281     // private
13282     findRecord : function(prop, value){
13283         var record;
13284         if(this.store.getCount() > 0){
13285             this.store.each(function(r){
13286                 if(r.data[prop] == value){
13287                     record = r;
13288                     return false;
13289                 }
13290                 return true;
13291             });
13292         }
13293         return record;
13294     },
13295     
13296     getName: function()
13297     {
13298         // returns hidden if it's set..
13299         if (!this.rendered) {return ''};
13300         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13301         
13302     },
13303     // private
13304     onViewMove : function(e, t){
13305         this.inKeyMode = false;
13306     },
13307
13308     // private
13309     onViewOver : function(e, t){
13310         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13311             return;
13312         }
13313         var item = this.view.findItemFromChild(t);
13314         
13315         if(item){
13316             var index = this.view.indexOf(item);
13317             this.select(index, false);
13318         }
13319     },
13320
13321     // private
13322     onViewClick : function(view, doFocus, el, e)
13323     {
13324         var index = this.view.getSelectedIndexes()[0];
13325         
13326         var r = this.store.getAt(index);
13327         
13328         if(this.tickable){
13329             
13330             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13331                 return;
13332             }
13333             
13334             var rm = false;
13335             var _this = this;
13336             
13337             Roo.each(this.tickItems, function(v,k){
13338                 
13339                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13340                     Roo.log(v);
13341                     _this.tickItems.splice(k, 1);
13342                     
13343                     if(typeof(e) == 'undefined' && view == false){
13344                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13345                     }
13346                     
13347                     rm = true;
13348                     return;
13349                 }
13350             });
13351             
13352             if(rm){
13353                 return;
13354             }
13355             
13356             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13357                 this.tickItems.push(r.data);
13358             }
13359             
13360             if(typeof(e) == 'undefined' && view == false){
13361                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13362             }
13363                     
13364             return;
13365         }
13366         
13367         if(r){
13368             this.onSelect(r, index);
13369         }
13370         if(doFocus !== false && !this.blockFocus){
13371             this.inputEl().focus();
13372         }
13373     },
13374
13375     // private
13376     restrictHeight : function(){
13377         //this.innerList.dom.style.height = '';
13378         //var inner = this.innerList.dom;
13379         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13380         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13381         //this.list.beginUpdate();
13382         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13383         this.list.alignTo(this.inputEl(), this.listAlign);
13384         this.list.alignTo(this.inputEl(), this.listAlign);
13385         //this.list.endUpdate();
13386     },
13387
13388     // private
13389     onEmptyResults : function(){
13390         
13391         if(this.tickable && this.editable){
13392             this.restrictHeight();
13393             return;
13394         }
13395         
13396         this.collapse();
13397     },
13398
13399     /**
13400      * Returns true if the dropdown list is expanded, else false.
13401      */
13402     isExpanded : function(){
13403         return this.list.isVisible();
13404     },
13405
13406     /**
13407      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13408      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13409      * @param {String} value The data value of the item to select
13410      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13411      * selected item if it is not currently in view (defaults to true)
13412      * @return {Boolean} True if the value matched an item in the list, else false
13413      */
13414     selectByValue : function(v, scrollIntoView){
13415         if(v !== undefined && v !== null){
13416             var r = this.findRecord(this.valueField || this.displayField, v);
13417             if(r){
13418                 this.select(this.store.indexOf(r), scrollIntoView);
13419                 return true;
13420             }
13421         }
13422         return false;
13423     },
13424
13425     /**
13426      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13427      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13428      * @param {Number} index The zero-based index of the list item to select
13429      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13430      * selected item if it is not currently in view (defaults to true)
13431      */
13432     select : function(index, scrollIntoView){
13433         this.selectedIndex = index;
13434         this.view.select(index);
13435         if(scrollIntoView !== false){
13436             var el = this.view.getNode(index);
13437             /*
13438              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13439              */
13440             if(el){
13441                 this.list.scrollChildIntoView(el, false);
13442             }
13443         }
13444     },
13445
13446     // private
13447     selectNext : function(){
13448         var ct = this.store.getCount();
13449         if(ct > 0){
13450             if(this.selectedIndex == -1){
13451                 this.select(0);
13452             }else if(this.selectedIndex < ct-1){
13453                 this.select(this.selectedIndex+1);
13454             }
13455         }
13456     },
13457
13458     // private
13459     selectPrev : function(){
13460         var ct = this.store.getCount();
13461         if(ct > 0){
13462             if(this.selectedIndex == -1){
13463                 this.select(0);
13464             }else if(this.selectedIndex != 0){
13465                 this.select(this.selectedIndex-1);
13466             }
13467         }
13468     },
13469
13470     // private
13471     onKeyUp : function(e){
13472         if(this.editable !== false && !e.isSpecialKey()){
13473             this.lastKey = e.getKey();
13474             this.dqTask.delay(this.queryDelay);
13475         }
13476     },
13477
13478     // private
13479     validateBlur : function(){
13480         return !this.list || !this.list.isVisible();   
13481     },
13482
13483     // private
13484     initQuery : function(){
13485         
13486         var v = this.getRawValue();
13487         
13488         if(this.tickable && this.editable){
13489             v = this.tickableInputEl().getValue();
13490         }
13491         
13492         this.doQuery(v);
13493     },
13494
13495     // private
13496     doForce : function(){
13497         if(this.inputEl().dom.value.length > 0){
13498             this.inputEl().dom.value =
13499                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13500              
13501         }
13502     },
13503
13504     /**
13505      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13506      * query allowing the query action to be canceled if needed.
13507      * @param {String} query The SQL query to execute
13508      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13509      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13510      * saved in the current store (defaults to false)
13511      */
13512     doQuery : function(q, forceAll){
13513         
13514         if(q === undefined || q === null){
13515             q = '';
13516         }
13517         var qe = {
13518             query: q,
13519             forceAll: forceAll,
13520             combo: this,
13521             cancel:false
13522         };
13523         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13524             return false;
13525         }
13526         q = qe.query;
13527         
13528         forceAll = qe.forceAll;
13529         if(forceAll === true || (q.length >= this.minChars)){
13530             
13531             this.hasQuery = true;
13532             
13533             if(this.lastQuery != q || this.alwaysQuery){
13534                 this.lastQuery = q;
13535                 if(this.mode == 'local'){
13536                     this.selectedIndex = -1;
13537                     if(forceAll){
13538                         this.store.clearFilter();
13539                     }else{
13540                         
13541                         if(this.specialFilter){
13542                             this.fireEvent('specialfilter', this);
13543                             this.onLoad();
13544                             return;
13545                         }
13546                         
13547                         this.store.filter(this.displayField, q);
13548                     }
13549                     
13550                     this.store.fireEvent("datachanged", this.store);
13551                     
13552                     this.onLoad();
13553                     
13554                     
13555                 }else{
13556                     
13557                     this.store.baseParams[this.queryParam] = q;
13558                     
13559                     var options = {params : this.getParams(q)};
13560                     
13561                     if(this.loadNext){
13562                         options.add = true;
13563                         options.params.start = this.page * this.pageSize;
13564                     }
13565                     
13566                     this.store.load(options);
13567                     
13568                     /*
13569                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13570                      *  we should expand the list on onLoad
13571                      *  so command out it
13572                      */
13573 //                    this.expand();
13574                 }
13575             }else{
13576                 this.selectedIndex = -1;
13577                 this.onLoad();   
13578             }
13579         }
13580         
13581         this.loadNext = false;
13582     },
13583     
13584     // private
13585     getParams : function(q){
13586         var p = {};
13587         //p[this.queryParam] = q;
13588         
13589         if(this.pageSize){
13590             p.start = 0;
13591             p.limit = this.pageSize;
13592         }
13593         return p;
13594     },
13595
13596     /**
13597      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13598      */
13599     collapse : function(){
13600         if(!this.isExpanded()){
13601             return;
13602         }
13603         
13604         this.list.hide();
13605         
13606         if(this.tickable){
13607             this.hasFocus = false;
13608             this.okBtn.hide();
13609             this.cancelBtn.hide();
13610             this.trigger.show();
13611             
13612             if(this.editable){
13613                 this.tickableInputEl().dom.value = '';
13614                 this.tickableInputEl().blur();
13615             }
13616             
13617         }
13618         
13619         Roo.get(document).un('mousedown', this.collapseIf, this);
13620         Roo.get(document).un('mousewheel', this.collapseIf, this);
13621         if (!this.editable) {
13622             Roo.get(document).un('keydown', this.listKeyPress, this);
13623         }
13624         this.fireEvent('collapse', this);
13625         
13626         this.validate();
13627     },
13628
13629     // private
13630     collapseIf : function(e){
13631         var in_combo  = e.within(this.el);
13632         var in_list =  e.within(this.list);
13633         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13634         
13635         if (in_combo || in_list || is_list) {
13636             //e.stopPropagation();
13637             return;
13638         }
13639         
13640         if(this.tickable){
13641             this.onTickableFooterButtonClick(e, false, false);
13642         }
13643
13644         this.collapse();
13645         
13646     },
13647
13648     /**
13649      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13650      */
13651     expand : function(){
13652        
13653         if(this.isExpanded() || !this.hasFocus){
13654             return;
13655         }
13656         
13657         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13658         this.list.setWidth(lw);
13659         
13660         
13661          Roo.log('expand');
13662         
13663         this.list.show();
13664         
13665         this.restrictHeight();
13666         
13667         if(this.tickable){
13668             
13669             this.tickItems = Roo.apply([], this.item);
13670             
13671             this.okBtn.show();
13672             this.cancelBtn.show();
13673             this.trigger.hide();
13674             
13675             if(this.editable){
13676                 this.tickableInputEl().focus();
13677             }
13678             
13679         }
13680         
13681         Roo.get(document).on('mousedown', this.collapseIf, this);
13682         Roo.get(document).on('mousewheel', this.collapseIf, this);
13683         if (!this.editable) {
13684             Roo.get(document).on('keydown', this.listKeyPress, this);
13685         }
13686         
13687         this.fireEvent('expand', this);
13688     },
13689
13690     // private
13691     // Implements the default empty TriggerField.onTriggerClick function
13692     onTriggerClick : function(e)
13693     {
13694         Roo.log('trigger click');
13695         
13696         if(this.disabled || !this.triggerList){
13697             return;
13698         }
13699         
13700         this.page = 0;
13701         this.loadNext = false;
13702         
13703         if(this.isExpanded()){
13704             this.collapse();
13705             if (!this.blockFocus) {
13706                 this.inputEl().focus();
13707             }
13708             
13709         }else {
13710             this.hasFocus = true;
13711             if(this.triggerAction == 'all') {
13712                 this.doQuery(this.allQuery, true);
13713             } else {
13714                 this.doQuery(this.getRawValue());
13715             }
13716             if (!this.blockFocus) {
13717                 this.inputEl().focus();
13718             }
13719         }
13720     },
13721     
13722     onTickableTriggerClick : function(e)
13723     {
13724         if(this.disabled){
13725             return;
13726         }
13727         
13728         this.page = 0;
13729         this.loadNext = false;
13730         this.hasFocus = true;
13731         
13732         if(this.triggerAction == 'all') {
13733             this.doQuery(this.allQuery, true);
13734         } else {
13735             this.doQuery(this.getRawValue());
13736         }
13737     },
13738     
13739     onSearchFieldClick : function(e)
13740     {
13741         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13742             this.onTickableFooterButtonClick(e, false, false);
13743             return;
13744         }
13745         
13746         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13747             return;
13748         }
13749         
13750         this.page = 0;
13751         this.loadNext = false;
13752         this.hasFocus = true;
13753         
13754         if(this.triggerAction == 'all') {
13755             this.doQuery(this.allQuery, true);
13756         } else {
13757             this.doQuery(this.getRawValue());
13758         }
13759     },
13760     
13761     listKeyPress : function(e)
13762     {
13763         //Roo.log('listkeypress');
13764         // scroll to first matching element based on key pres..
13765         if (e.isSpecialKey()) {
13766             return false;
13767         }
13768         var k = String.fromCharCode(e.getKey()).toUpperCase();
13769         //Roo.log(k);
13770         var match  = false;
13771         var csel = this.view.getSelectedNodes();
13772         var cselitem = false;
13773         if (csel.length) {
13774             var ix = this.view.indexOf(csel[0]);
13775             cselitem  = this.store.getAt(ix);
13776             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13777                 cselitem = false;
13778             }
13779             
13780         }
13781         
13782         this.store.each(function(v) { 
13783             if (cselitem) {
13784                 // start at existing selection.
13785                 if (cselitem.id == v.id) {
13786                     cselitem = false;
13787                 }
13788                 return true;
13789             }
13790                 
13791             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13792                 match = this.store.indexOf(v);
13793                 return false;
13794             }
13795             return true;
13796         }, this);
13797         
13798         if (match === false) {
13799             return true; // no more action?
13800         }
13801         // scroll to?
13802         this.view.select(match);
13803         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13804         sn.scrollIntoView(sn.dom.parentNode, false);
13805     },
13806     
13807     onViewScroll : function(e, t){
13808         
13809         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){
13810             return;
13811         }
13812         
13813         this.hasQuery = true;
13814         
13815         this.loading = this.list.select('.loading', true).first();
13816         
13817         if(this.loading === null){
13818             this.list.createChild({
13819                 tag: 'div',
13820                 cls: 'loading roo-select2-more-results roo-select2-active',
13821                 html: 'Loading more results...'
13822             });
13823             
13824             this.loading = this.list.select('.loading', true).first();
13825             
13826             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13827             
13828             this.loading.hide();
13829         }
13830         
13831         this.loading.show();
13832         
13833         var _combo = this;
13834         
13835         this.page++;
13836         this.loadNext = true;
13837         
13838         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13839         
13840         return;
13841     },
13842     
13843     addItem : function(o)
13844     {   
13845         var dv = ''; // display value
13846         
13847         if (this.displayField) {
13848             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13849         } else {
13850             // this is an error condition!!!
13851             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13852         }
13853         
13854         if(!dv.length){
13855             return;
13856         }
13857         
13858         var choice = this.choices.createChild({
13859             tag: 'li',
13860             cls: 'roo-select2-search-choice',
13861             cn: [
13862                 {
13863                     tag: 'div',
13864                     html: dv
13865                 },
13866                 {
13867                     tag: 'a',
13868                     href: '#',
13869                     cls: 'roo-select2-search-choice-close',
13870                     tabindex: '-1'
13871                 }
13872             ]
13873             
13874         }, this.searchField);
13875         
13876         var close = choice.select('a.roo-select2-search-choice-close', true).first();
13877         
13878         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13879         
13880         this.item.push(o);
13881         
13882         this.lastData = o;
13883         
13884         this.syncValue();
13885         
13886         this.inputEl().dom.value = '';
13887         
13888         this.validate();
13889     },
13890     
13891     onRemoveItem : function(e, _self, o)
13892     {
13893         e.preventDefault();
13894         
13895         this.lastItem = Roo.apply([], this.item);
13896         
13897         var index = this.item.indexOf(o.data) * 1;
13898         
13899         if( index < 0){
13900             Roo.log('not this item?!');
13901             return;
13902         }
13903         
13904         this.item.splice(index, 1);
13905         o.item.remove();
13906         
13907         this.syncValue();
13908         
13909         this.fireEvent('remove', this, e);
13910         
13911         this.validate();
13912         
13913     },
13914     
13915     syncValue : function()
13916     {
13917         if(!this.item.length){
13918             this.clearValue();
13919             return;
13920         }
13921             
13922         var value = [];
13923         var _this = this;
13924         Roo.each(this.item, function(i){
13925             if(_this.valueField){
13926                 value.push(i[_this.valueField]);
13927                 return;
13928             }
13929
13930             value.push(i);
13931         });
13932
13933         this.value = value.join(',');
13934
13935         if(this.hiddenField){
13936             this.hiddenField.dom.value = this.value;
13937         }
13938         
13939         this.store.fireEvent("datachanged", this.store);
13940         
13941         this.validate();
13942     },
13943     
13944     clearItem : function()
13945     {
13946         if(!this.multiple){
13947             return;
13948         }
13949         
13950         this.item = [];
13951         
13952         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13953            c.remove();
13954         });
13955         
13956         this.syncValue();
13957         
13958         this.validate();
13959         
13960         if(this.tickable && !Roo.isTouch){
13961             this.view.refresh();
13962         }
13963     },
13964     
13965     inputEl: function ()
13966     {
13967         if(Roo.isIOS && this.useNativeIOS){
13968             return this.el.select('select.roo-ios-select', true).first();
13969         }
13970         
13971         if(Roo.isTouch && this.mobileTouchView){
13972             return this.el.select('input.form-control',true).first();
13973         }
13974         
13975         if(this.tickable){
13976             return this.searchField;
13977         }
13978         
13979         return this.el.select('input.form-control',true).first();
13980     },
13981     
13982     onTickableFooterButtonClick : function(e, btn, el)
13983     {
13984         e.preventDefault();
13985         
13986         this.lastItem = Roo.apply([], this.item);
13987         
13988         if(btn && btn.name == 'cancel'){
13989             this.tickItems = Roo.apply([], this.item);
13990             this.collapse();
13991             return;
13992         }
13993         
13994         this.clearItem();
13995         
13996         var _this = this;
13997         
13998         Roo.each(this.tickItems, function(o){
13999             _this.addItem(o);
14000         });
14001         
14002         this.collapse();
14003         
14004     },
14005     
14006     validate : function()
14007     {
14008         var v = this.getRawValue();
14009         
14010         if(this.multiple){
14011             v = this.getValue();
14012         }
14013         
14014         if(this.disabled || this.allowBlank || v.length){
14015             this.markValid();
14016             return true;
14017         }
14018         
14019         this.markInvalid();
14020         return false;
14021     },
14022     
14023     tickableInputEl : function()
14024     {
14025         if(!this.tickable || !this.editable){
14026             return this.inputEl();
14027         }
14028         
14029         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14030     },
14031     
14032     
14033     getAutoCreateTouchView : function()
14034     {
14035         var id = Roo.id();
14036         
14037         var cfg = {
14038             cls: 'form-group' //input-group
14039         };
14040         
14041         var input =  {
14042             tag: 'input',
14043             id : id,
14044             type : this.inputType,
14045             cls : 'form-control x-combo-noedit',
14046             autocomplete: 'new-password',
14047             placeholder : this.placeholder || '',
14048             readonly : true
14049         };
14050         
14051         if (this.name) {
14052             input.name = this.name;
14053         }
14054         
14055         if (this.size) {
14056             input.cls += ' input-' + this.size;
14057         }
14058         
14059         if (this.disabled) {
14060             input.disabled = true;
14061         }
14062         
14063         var inputblock = {
14064             cls : '',
14065             cn : [
14066                 input
14067             ]
14068         };
14069         
14070         if(this.before){
14071             inputblock.cls += ' input-group';
14072             
14073             inputblock.cn.unshift({
14074                 tag :'span',
14075                 cls : 'input-group-addon',
14076                 html : this.before
14077             });
14078         }
14079         
14080         if(this.removable && !this.multiple){
14081             inputblock.cls += ' roo-removable';
14082             
14083             inputblock.cn.push({
14084                 tag: 'button',
14085                 html : 'x',
14086                 cls : 'roo-combo-removable-btn close'
14087             });
14088         }
14089
14090         if(this.hasFeedback && !this.allowBlank){
14091             
14092             inputblock.cls += ' has-feedback';
14093             
14094             inputblock.cn.push({
14095                 tag: 'span',
14096                 cls: 'glyphicon form-control-feedback'
14097             });
14098             
14099         }
14100         
14101         if (this.after) {
14102             
14103             inputblock.cls += (this.before) ? '' : ' input-group';
14104             
14105             inputblock.cn.push({
14106                 tag :'span',
14107                 cls : 'input-group-addon',
14108                 html : this.after
14109             });
14110         }
14111
14112         var box = {
14113             tag: 'div',
14114             cn: [
14115                 {
14116                     tag: 'input',
14117                     type : 'hidden',
14118                     cls: 'form-hidden-field'
14119                 },
14120                 inputblock
14121             ]
14122             
14123         };
14124         
14125         if(this.multiple){
14126             box = {
14127                 tag: 'div',
14128                 cn: [
14129                     {
14130                         tag: 'input',
14131                         type : 'hidden',
14132                         cls: 'form-hidden-field'
14133                     },
14134                     {
14135                         tag: 'ul',
14136                         cls: 'roo-select2-choices',
14137                         cn:[
14138                             {
14139                                 tag: 'li',
14140                                 cls: 'roo-select2-search-field',
14141                                 cn: [
14142
14143                                     inputblock
14144                                 ]
14145                             }
14146                         ]
14147                     }
14148                 ]
14149             }
14150         };
14151         
14152         var combobox = {
14153             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14154             cn: [
14155                 box
14156             ]
14157         };
14158         
14159         if(!this.multiple && this.showToggleBtn){
14160             
14161             var caret = {
14162                         tag: 'span',
14163                         cls: 'caret'
14164             };
14165             
14166             if (this.caret != false) {
14167                 caret = {
14168                      tag: 'i',
14169                      cls: 'fa fa-' + this.caret
14170                 };
14171                 
14172             }
14173             
14174             combobox.cn.push({
14175                 tag :'span',
14176                 cls : 'input-group-addon btn dropdown-toggle',
14177                 cn : [
14178                     caret,
14179                     {
14180                         tag: 'span',
14181                         cls: 'combobox-clear',
14182                         cn  : [
14183                             {
14184                                 tag : 'i',
14185                                 cls: 'icon-remove'
14186                             }
14187                         ]
14188                     }
14189                 ]
14190
14191             })
14192         }
14193         
14194         if(this.multiple){
14195             combobox.cls += ' roo-select2-container-multi';
14196         }
14197         
14198         var align = this.labelAlign || this.parentLabelAlign();
14199         
14200         cfg.cn = combobox;
14201         
14202         if(this.fieldLabel.length && this.labelWidth){
14203             
14204             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
14205             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
14206             
14207             cfg.cn = [
14208                 {
14209                    tag : 'i',
14210                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14211                    tooltip : 'This field is required'
14212                 },
14213                 {
14214                     tag: 'label',
14215                     cls : 'control-label ' + lw,
14216                     html : this.fieldLabel
14217
14218                 },
14219                 {
14220                     cls : cw, 
14221                     cn: [
14222                         combobox
14223                     ]
14224                 }
14225             ];
14226             
14227             if(this.indicatorpos == 'right'){
14228                 cfg.cn = [
14229                     {
14230                         tag: 'label',
14231                         cls : 'control-label ' + lw,
14232                         html : this.fieldLabel
14233
14234                     },
14235                     {
14236                        tag : 'i',
14237                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14238                        tooltip : 'This field is required'
14239                     },
14240                     {
14241                         cls : cw, 
14242                         cn: [
14243                             combobox
14244                         ]
14245                     }
14246                 ];
14247             }
14248         }
14249         
14250         var settings = this;
14251         
14252         ['xs','sm','md','lg'].map(function(size){
14253             if (settings[size]) {
14254                 cfg.cls += ' col-' + size + '-' + settings[size];
14255             }
14256         });
14257         
14258         return cfg;
14259     },
14260     
14261     initTouchView : function()
14262     {
14263         this.renderTouchView();
14264         
14265         this.touchViewEl.on('scroll', function(){
14266             this.el.dom.scrollTop = 0;
14267         }, this);
14268         
14269         this.originalValue = this.getValue();
14270         
14271         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14272         
14273         this.inputEl().on("click", this.showTouchView, this);
14274         if (this.triggerEl) {
14275             this.triggerEl.on("click", this.showTouchView, this);
14276         }
14277         
14278         
14279         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14280         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14281         
14282         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14283         
14284         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14285         this.store.on('load', this.onTouchViewLoad, this);
14286         this.store.on('loadexception', this.onTouchViewLoadException, this);
14287         
14288         if(this.hiddenName){
14289             
14290             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14291             
14292             this.hiddenField.dom.value =
14293                 this.hiddenValue !== undefined ? this.hiddenValue :
14294                 this.value !== undefined ? this.value : '';
14295         
14296             this.el.dom.removeAttribute('name');
14297             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14298         }
14299         
14300         if(this.multiple){
14301             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14302             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14303         }
14304         
14305         if(this.removable && !this.multiple){
14306             var close = this.closeTriggerEl();
14307             if(close){
14308                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14309                 close.on('click', this.removeBtnClick, this, close);
14310             }
14311         }
14312         /*
14313          * fix the bug in Safari iOS8
14314          */
14315         this.inputEl().on("focus", function(e){
14316             document.activeElement.blur();
14317         }, this);
14318         
14319         return;
14320         
14321         
14322     },
14323     
14324     renderTouchView : function()
14325     {
14326         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14327         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14328         
14329         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14330         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14331         
14332         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14333         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14334         this.touchViewBodyEl.setStyle('overflow', 'auto');
14335         
14336         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14337         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14338         
14339         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14340         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14341         
14342     },
14343     
14344     showTouchView : function()
14345     {
14346         if(this.disabled){
14347             return;
14348         }
14349         
14350         this.touchViewHeaderEl.hide();
14351
14352         if(this.modalTitle.length){
14353             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14354             this.touchViewHeaderEl.show();
14355         }
14356
14357         this.touchViewEl.show();
14358
14359         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14360         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14361                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14362
14363         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14364
14365         if(this.modalTitle.length){
14366             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14367         }
14368         
14369         this.touchViewBodyEl.setHeight(bodyHeight);
14370
14371         if(this.animate){
14372             var _this = this;
14373             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14374         }else{
14375             this.touchViewEl.addClass('in');
14376         }
14377
14378         this.doTouchViewQuery();
14379         
14380     },
14381     
14382     hideTouchView : function()
14383     {
14384         this.touchViewEl.removeClass('in');
14385
14386         if(this.animate){
14387             var _this = this;
14388             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14389         }else{
14390             this.touchViewEl.setStyle('display', 'none');
14391         }
14392         
14393     },
14394     
14395     setTouchViewValue : function()
14396     {
14397         if(this.multiple){
14398             this.clearItem();
14399         
14400             var _this = this;
14401
14402             Roo.each(this.tickItems, function(o){
14403                 this.addItem(o);
14404             }, this);
14405         }
14406         
14407         this.hideTouchView();
14408     },
14409     
14410     doTouchViewQuery : function()
14411     {
14412         var qe = {
14413             query: '',
14414             forceAll: true,
14415             combo: this,
14416             cancel:false
14417         };
14418         
14419         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14420             return false;
14421         }
14422         
14423         if(!this.alwaysQuery || this.mode == 'local'){
14424             this.onTouchViewLoad();
14425             return;
14426         }
14427         
14428         this.store.load();
14429     },
14430     
14431     onTouchViewBeforeLoad : function(combo,opts)
14432     {
14433         return;
14434     },
14435
14436     // private
14437     onTouchViewLoad : function()
14438     {
14439         if(this.store.getCount() < 1){
14440             this.onTouchViewEmptyResults();
14441             return;
14442         }
14443         
14444         this.clearTouchView();
14445         
14446         var rawValue = this.getRawValue();
14447         
14448         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14449         
14450         this.tickItems = [];
14451         
14452         this.store.data.each(function(d, rowIndex){
14453             var row = this.touchViewListGroup.createChild(template);
14454             
14455             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14456                 row.addClass(d.data.cls);
14457             }
14458             
14459             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14460                 var cfg = {
14461                     data : d.data,
14462                     html : d.data[this.displayField]
14463                 };
14464                 
14465                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14466                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14467                 }
14468             }
14469             row.removeClass('selected');
14470             if(!this.multiple && this.valueField &&
14471                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14472             {
14473                 // radio buttons..
14474                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14475                 row.addClass('selected');
14476             }
14477             
14478             if(this.multiple && this.valueField &&
14479                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14480             {
14481                 
14482                 // checkboxes...
14483                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14484                 this.tickItems.push(d.data);
14485             }
14486             
14487             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14488             
14489         }, this);
14490         
14491         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14492         
14493         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14494
14495         if(this.modalTitle.length){
14496             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14497         }
14498
14499         var listHeight = this.touchViewListGroup.getHeight();
14500         
14501         var _this = this;
14502         
14503         if(firstChecked && listHeight > bodyHeight){
14504             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14505         }
14506         
14507     },
14508     
14509     onTouchViewLoadException : function()
14510     {
14511         this.hideTouchView();
14512     },
14513     
14514     onTouchViewEmptyResults : function()
14515     {
14516         this.clearTouchView();
14517         
14518         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14519         
14520         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14521         
14522     },
14523     
14524     clearTouchView : function()
14525     {
14526         this.touchViewListGroup.dom.innerHTML = '';
14527     },
14528     
14529     onTouchViewClick : function(e, el, o)
14530     {
14531         e.preventDefault();
14532         
14533         var row = o.row;
14534         var rowIndex = o.rowIndex;
14535         
14536         var r = this.store.getAt(rowIndex);
14537         
14538         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14539             
14540             if(!this.multiple){
14541                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14542                     c.dom.removeAttribute('checked');
14543                 }, this);
14544
14545                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14546
14547                 this.setFromData(r.data);
14548
14549                 var close = this.closeTriggerEl();
14550
14551                 if(close){
14552                     close.show();
14553                 }
14554
14555                 this.hideTouchView();
14556
14557                 this.fireEvent('select', this, r, rowIndex);
14558
14559                 return;
14560             }
14561
14562             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14563                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14564                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14565                 return;
14566             }
14567
14568             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14569             this.addItem(r.data);
14570             this.tickItems.push(r.data);
14571         }
14572     },
14573     
14574     getAutoCreateNativeIOS : function()
14575     {
14576         var cfg = {
14577             cls: 'form-group' //input-group,
14578         };
14579         
14580         var combobox =  {
14581             tag: 'select',
14582             cls : 'roo-ios-select'
14583         };
14584         
14585         if (this.name) {
14586             combobox.name = this.name;
14587         }
14588         
14589         if (this.disabled) {
14590             combobox.disabled = true;
14591         }
14592         
14593         var settings = this;
14594         
14595         ['xs','sm','md','lg'].map(function(size){
14596             if (settings[size]) {
14597                 cfg.cls += ' col-' + size + '-' + settings[size];
14598             }
14599         });
14600         
14601         cfg.cn = combobox;
14602         
14603         return cfg;
14604         
14605     },
14606     
14607     initIOSView : function()
14608     {
14609         this.store.on('load', this.onIOSViewLoad, this);
14610         
14611         return;
14612     },
14613     
14614     onIOSViewLoad : function()
14615     {
14616         if(this.store.getCount() < 1){
14617             return;
14618         }
14619         
14620         this.clearIOSView();
14621         
14622         if(this.allowBlank) {
14623             
14624             var default_text = '-- SELECT --';
14625             
14626             var opt = this.inputEl().createChild({
14627                 tag: 'option',
14628                 value : 0,
14629                 html : default_text
14630             });
14631             
14632             var o = {};
14633             o[this.valueField] = 0;
14634             o[this.displayField] = default_text;
14635             
14636             this.ios_options.push({
14637                 data : o,
14638                 el : opt
14639             });
14640             
14641         }
14642         
14643         this.store.data.each(function(d, rowIndex){
14644             
14645             var html = '';
14646             
14647             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14648                 html = d.data[this.displayField];
14649             }
14650             
14651             var value = '';
14652             
14653             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
14654                 value = d.data[this.valueField];
14655             }
14656             
14657             var option = {
14658                 tag: 'option',
14659                 value : value,
14660                 html : html
14661             };
14662             
14663             if(this.value == d.data[this.valueField]){
14664                 option['selected'] = true;
14665             }
14666             
14667             var opt = this.inputEl().createChild(option);
14668             
14669             this.ios_options.push({
14670                 data : d.data,
14671                 el : opt
14672             });
14673             
14674         }, this);
14675         
14676         this.inputEl().on('change', function(){
14677            this.fireEvent('select', this);
14678         }, this);
14679         
14680     },
14681     
14682     clearIOSView: function()
14683     {
14684         this.inputEl().dom.innerHTML = '';
14685         
14686         this.ios_options = [];
14687     },
14688     
14689     setIOSValue: function(v)
14690     {
14691         this.value = v;
14692         
14693         if(!this.ios_options){
14694             return;
14695         }
14696         
14697         Roo.each(this.ios_options, function(opts){
14698            
14699            opts.el.dom.removeAttribute('selected');
14700            
14701            if(opts.data[this.valueField] != v){
14702                return;
14703            }
14704            
14705            opts.el.dom.setAttribute('selected', true);
14706            
14707         }, this);
14708     }
14709
14710     /** 
14711     * @cfg {Boolean} grow 
14712     * @hide 
14713     */
14714     /** 
14715     * @cfg {Number} growMin 
14716     * @hide 
14717     */
14718     /** 
14719     * @cfg {Number} growMax 
14720     * @hide 
14721     */
14722     /**
14723      * @hide
14724      * @method autoSize
14725      */
14726 });
14727
14728 Roo.apply(Roo.bootstrap.ComboBox,  {
14729     
14730     header : {
14731         tag: 'div',
14732         cls: 'modal-header',
14733         cn: [
14734             {
14735                 tag: 'h4',
14736                 cls: 'modal-title'
14737             }
14738         ]
14739     },
14740     
14741     body : {
14742         tag: 'div',
14743         cls: 'modal-body',
14744         cn: [
14745             {
14746                 tag: 'ul',
14747                 cls: 'list-group'
14748             }
14749         ]
14750     },
14751     
14752     listItemRadio : {
14753         tag: 'li',
14754         cls: 'list-group-item',
14755         cn: [
14756             {
14757                 tag: 'span',
14758                 cls: 'roo-combobox-list-group-item-value'
14759             },
14760             {
14761                 tag: 'div',
14762                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14763                 cn: [
14764                     {
14765                         tag: 'input',
14766                         type: 'radio'
14767                     },
14768                     {
14769                         tag: 'label'
14770                     }
14771                 ]
14772             }
14773         ]
14774     },
14775     
14776     listItemCheckbox : {
14777         tag: 'li',
14778         cls: 'list-group-item',
14779         cn: [
14780             {
14781                 tag: 'span',
14782                 cls: 'roo-combobox-list-group-item-value'
14783             },
14784             {
14785                 tag: 'div',
14786                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14787                 cn: [
14788                     {
14789                         tag: 'input',
14790                         type: 'checkbox'
14791                     },
14792                     {
14793                         tag: 'label'
14794                     }
14795                 ]
14796             }
14797         ]
14798     },
14799     
14800     emptyResult : {
14801         tag: 'div',
14802         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14803     },
14804     
14805     footer : {
14806         tag: 'div',
14807         cls: 'modal-footer',
14808         cn: [
14809             {
14810                 tag: 'div',
14811                 cls: 'row',
14812                 cn: [
14813                     {
14814                         tag: 'div',
14815                         cls: 'col-xs-6 text-left',
14816                         cn: {
14817                             tag: 'button',
14818                             cls: 'btn btn-danger roo-touch-view-cancel',
14819                             html: 'Cancel'
14820                         }
14821                     },
14822                     {
14823                         tag: 'div',
14824                         cls: 'col-xs-6 text-right',
14825                         cn: {
14826                             tag: 'button',
14827                             cls: 'btn btn-success roo-touch-view-ok',
14828                             html: 'OK'
14829                         }
14830                     }
14831                 ]
14832             }
14833         ]
14834         
14835     }
14836 });
14837
14838 Roo.apply(Roo.bootstrap.ComboBox,  {
14839     
14840     touchViewTemplate : {
14841         tag: 'div',
14842         cls: 'modal fade roo-combobox-touch-view',
14843         cn: [
14844             {
14845                 tag: 'div',
14846                 cls: 'modal-dialog',
14847                 style : 'position:fixed', // we have to fix position....
14848                 cn: [
14849                     {
14850                         tag: 'div',
14851                         cls: 'modal-content',
14852                         cn: [
14853                             Roo.bootstrap.ComboBox.header,
14854                             Roo.bootstrap.ComboBox.body,
14855                             Roo.bootstrap.ComboBox.footer
14856                         ]
14857                     }
14858                 ]
14859             }
14860         ]
14861     }
14862 });/*
14863  * Based on:
14864  * Ext JS Library 1.1.1
14865  * Copyright(c) 2006-2007, Ext JS, LLC.
14866  *
14867  * Originally Released Under LGPL - original licence link has changed is not relivant.
14868  *
14869  * Fork - LGPL
14870  * <script type="text/javascript">
14871  */
14872
14873 /**
14874  * @class Roo.View
14875  * @extends Roo.util.Observable
14876  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14877  * This class also supports single and multi selection modes. <br>
14878  * Create a data model bound view:
14879  <pre><code>
14880  var store = new Roo.data.Store(...);
14881
14882  var view = new Roo.View({
14883     el : "my-element",
14884     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14885  
14886     singleSelect: true,
14887     selectedClass: "ydataview-selected",
14888     store: store
14889  });
14890
14891  // listen for node click?
14892  view.on("click", function(vw, index, node, e){
14893  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14894  });
14895
14896  // load XML data
14897  dataModel.load("foobar.xml");
14898  </code></pre>
14899  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14900  * <br><br>
14901  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14902  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14903  * 
14904  * Note: old style constructor is still suported (container, template, config)
14905  * 
14906  * @constructor
14907  * Create a new View
14908  * @param {Object} config The config object
14909  * 
14910  */
14911 Roo.View = function(config, depreciated_tpl, depreciated_config){
14912     
14913     this.parent = false;
14914     
14915     if (typeof(depreciated_tpl) == 'undefined') {
14916         // new way.. - universal constructor.
14917         Roo.apply(this, config);
14918         this.el  = Roo.get(this.el);
14919     } else {
14920         // old format..
14921         this.el  = Roo.get(config);
14922         this.tpl = depreciated_tpl;
14923         Roo.apply(this, depreciated_config);
14924     }
14925     this.wrapEl  = this.el.wrap().wrap();
14926     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14927     
14928     
14929     if(typeof(this.tpl) == "string"){
14930         this.tpl = new Roo.Template(this.tpl);
14931     } else {
14932         // support xtype ctors..
14933         this.tpl = new Roo.factory(this.tpl, Roo);
14934     }
14935     
14936     
14937     this.tpl.compile();
14938     
14939     /** @private */
14940     this.addEvents({
14941         /**
14942          * @event beforeclick
14943          * Fires before a click is processed. Returns false to cancel the default action.
14944          * @param {Roo.View} this
14945          * @param {Number} index The index of the target node
14946          * @param {HTMLElement} node The target node
14947          * @param {Roo.EventObject} e The raw event object
14948          */
14949             "beforeclick" : true,
14950         /**
14951          * @event click
14952          * Fires when a template node is clicked.
14953          * @param {Roo.View} this
14954          * @param {Number} index The index of the target node
14955          * @param {HTMLElement} node The target node
14956          * @param {Roo.EventObject} e The raw event object
14957          */
14958             "click" : true,
14959         /**
14960          * @event dblclick
14961          * Fires when a template node is double clicked.
14962          * @param {Roo.View} this
14963          * @param {Number} index The index of the target node
14964          * @param {HTMLElement} node The target node
14965          * @param {Roo.EventObject} e The raw event object
14966          */
14967             "dblclick" : true,
14968         /**
14969          * @event contextmenu
14970          * Fires when a template node is right clicked.
14971          * @param {Roo.View} this
14972          * @param {Number} index The index of the target node
14973          * @param {HTMLElement} node The target node
14974          * @param {Roo.EventObject} e The raw event object
14975          */
14976             "contextmenu" : true,
14977         /**
14978          * @event selectionchange
14979          * Fires when the selected nodes change.
14980          * @param {Roo.View} this
14981          * @param {Array} selections Array of the selected nodes
14982          */
14983             "selectionchange" : true,
14984     
14985         /**
14986          * @event beforeselect
14987          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14988          * @param {Roo.View} this
14989          * @param {HTMLElement} node The node to be selected
14990          * @param {Array} selections Array of currently selected nodes
14991          */
14992             "beforeselect" : true,
14993         /**
14994          * @event preparedata
14995          * Fires on every row to render, to allow you to change the data.
14996          * @param {Roo.View} this
14997          * @param {Object} data to be rendered (change this)
14998          */
14999           "preparedata" : true
15000           
15001           
15002         });
15003
15004
15005
15006     this.el.on({
15007         "click": this.onClick,
15008         "dblclick": this.onDblClick,
15009         "contextmenu": this.onContextMenu,
15010         scope:this
15011     });
15012
15013     this.selections = [];
15014     this.nodes = [];
15015     this.cmp = new Roo.CompositeElementLite([]);
15016     if(this.store){
15017         this.store = Roo.factory(this.store, Roo.data);
15018         this.setStore(this.store, true);
15019     }
15020     
15021     if ( this.footer && this.footer.xtype) {
15022            
15023          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15024         
15025         this.footer.dataSource = this.store;
15026         this.footer.container = fctr;
15027         this.footer = Roo.factory(this.footer, Roo);
15028         fctr.insertFirst(this.el);
15029         
15030         // this is a bit insane - as the paging toolbar seems to detach the el..
15031 //        dom.parentNode.parentNode.parentNode
15032          // they get detached?
15033     }
15034     
15035     
15036     Roo.View.superclass.constructor.call(this);
15037     
15038     
15039 };
15040
15041 Roo.extend(Roo.View, Roo.util.Observable, {
15042     
15043      /**
15044      * @cfg {Roo.data.Store} store Data store to load data from.
15045      */
15046     store : false,
15047     
15048     /**
15049      * @cfg {String|Roo.Element} el The container element.
15050      */
15051     el : '',
15052     
15053     /**
15054      * @cfg {String|Roo.Template} tpl The template used by this View 
15055      */
15056     tpl : false,
15057     /**
15058      * @cfg {String} dataName the named area of the template to use as the data area
15059      *                          Works with domtemplates roo-name="name"
15060      */
15061     dataName: false,
15062     /**
15063      * @cfg {String} selectedClass The css class to add to selected nodes
15064      */
15065     selectedClass : "x-view-selected",
15066      /**
15067      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15068      */
15069     emptyText : "",
15070     
15071     /**
15072      * @cfg {String} text to display on mask (default Loading)
15073      */
15074     mask : false,
15075     /**
15076      * @cfg {Boolean} multiSelect Allow multiple selection
15077      */
15078     multiSelect : false,
15079     /**
15080      * @cfg {Boolean} singleSelect Allow single selection
15081      */
15082     singleSelect:  false,
15083     
15084     /**
15085      * @cfg {Boolean} toggleSelect - selecting 
15086      */
15087     toggleSelect : false,
15088     
15089     /**
15090      * @cfg {Boolean} tickable - selecting 
15091      */
15092     tickable : false,
15093     
15094     /**
15095      * Returns the element this view is bound to.
15096      * @return {Roo.Element}
15097      */
15098     getEl : function(){
15099         return this.wrapEl;
15100     },
15101     
15102     
15103
15104     /**
15105      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15106      */
15107     refresh : function(){
15108         //Roo.log('refresh');
15109         var t = this.tpl;
15110         
15111         // if we are using something like 'domtemplate', then
15112         // the what gets used is:
15113         // t.applySubtemplate(NAME, data, wrapping data..)
15114         // the outer template then get' applied with
15115         //     the store 'extra data'
15116         // and the body get's added to the
15117         //      roo-name="data" node?
15118         //      <span class='roo-tpl-{name}'></span> ?????
15119         
15120         
15121         
15122         this.clearSelections();
15123         this.el.update("");
15124         var html = [];
15125         var records = this.store.getRange();
15126         if(records.length < 1) {
15127             
15128             // is this valid??  = should it render a template??
15129             
15130             this.el.update(this.emptyText);
15131             return;
15132         }
15133         var el = this.el;
15134         if (this.dataName) {
15135             this.el.update(t.apply(this.store.meta)); //????
15136             el = this.el.child('.roo-tpl-' + this.dataName);
15137         }
15138         
15139         for(var i = 0, len = records.length; i < len; i++){
15140             var data = this.prepareData(records[i].data, i, records[i]);
15141             this.fireEvent("preparedata", this, data, i, records[i]);
15142             
15143             var d = Roo.apply({}, data);
15144             
15145             if(this.tickable){
15146                 Roo.apply(d, {'roo-id' : Roo.id()});
15147                 
15148                 var _this = this;
15149             
15150                 Roo.each(this.parent.item, function(item){
15151                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15152                         return;
15153                     }
15154                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15155                 });
15156             }
15157             
15158             html[html.length] = Roo.util.Format.trim(
15159                 this.dataName ?
15160                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15161                     t.apply(d)
15162             );
15163         }
15164         
15165         
15166         
15167         el.update(html.join(""));
15168         this.nodes = el.dom.childNodes;
15169         this.updateIndexes(0);
15170     },
15171     
15172
15173     /**
15174      * Function to override to reformat the data that is sent to
15175      * the template for each node.
15176      * DEPRICATED - use the preparedata event handler.
15177      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15178      * a JSON object for an UpdateManager bound view).
15179      */
15180     prepareData : function(data, index, record)
15181     {
15182         this.fireEvent("preparedata", this, data, index, record);
15183         return data;
15184     },
15185
15186     onUpdate : function(ds, record){
15187         // Roo.log('on update');   
15188         this.clearSelections();
15189         var index = this.store.indexOf(record);
15190         var n = this.nodes[index];
15191         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15192         n.parentNode.removeChild(n);
15193         this.updateIndexes(index, index);
15194     },
15195
15196     
15197     
15198 // --------- FIXME     
15199     onAdd : function(ds, records, index)
15200     {
15201         //Roo.log(['on Add', ds, records, index] );        
15202         this.clearSelections();
15203         if(this.nodes.length == 0){
15204             this.refresh();
15205             return;
15206         }
15207         var n = this.nodes[index];
15208         for(var i = 0, len = records.length; i < len; i++){
15209             var d = this.prepareData(records[i].data, i, records[i]);
15210             if(n){
15211                 this.tpl.insertBefore(n, d);
15212             }else{
15213                 
15214                 this.tpl.append(this.el, d);
15215             }
15216         }
15217         this.updateIndexes(index);
15218     },
15219
15220     onRemove : function(ds, record, index){
15221        // Roo.log('onRemove');
15222         this.clearSelections();
15223         var el = this.dataName  ?
15224             this.el.child('.roo-tpl-' + this.dataName) :
15225             this.el; 
15226         
15227         el.dom.removeChild(this.nodes[index]);
15228         this.updateIndexes(index);
15229     },
15230
15231     /**
15232      * Refresh an individual node.
15233      * @param {Number} index
15234      */
15235     refreshNode : function(index){
15236         this.onUpdate(this.store, this.store.getAt(index));
15237     },
15238
15239     updateIndexes : function(startIndex, endIndex){
15240         var ns = this.nodes;
15241         startIndex = startIndex || 0;
15242         endIndex = endIndex || ns.length - 1;
15243         for(var i = startIndex; i <= endIndex; i++){
15244             ns[i].nodeIndex = i;
15245         }
15246     },
15247
15248     /**
15249      * Changes the data store this view uses and refresh the view.
15250      * @param {Store} store
15251      */
15252     setStore : function(store, initial){
15253         if(!initial && this.store){
15254             this.store.un("datachanged", this.refresh);
15255             this.store.un("add", this.onAdd);
15256             this.store.un("remove", this.onRemove);
15257             this.store.un("update", this.onUpdate);
15258             this.store.un("clear", this.refresh);
15259             this.store.un("beforeload", this.onBeforeLoad);
15260             this.store.un("load", this.onLoad);
15261             this.store.un("loadexception", this.onLoad);
15262         }
15263         if(store){
15264           
15265             store.on("datachanged", this.refresh, this);
15266             store.on("add", this.onAdd, this);
15267             store.on("remove", this.onRemove, this);
15268             store.on("update", this.onUpdate, this);
15269             store.on("clear", this.refresh, this);
15270             store.on("beforeload", this.onBeforeLoad, this);
15271             store.on("load", this.onLoad, this);
15272             store.on("loadexception", this.onLoad, this);
15273         }
15274         
15275         if(store){
15276             this.refresh();
15277         }
15278     },
15279     /**
15280      * onbeforeLoad - masks the loading area.
15281      *
15282      */
15283     onBeforeLoad : function(store,opts)
15284     {
15285          //Roo.log('onBeforeLoad');   
15286         if (!opts.add) {
15287             this.el.update("");
15288         }
15289         this.el.mask(this.mask ? this.mask : "Loading" ); 
15290     },
15291     onLoad : function ()
15292     {
15293         this.el.unmask();
15294     },
15295     
15296
15297     /**
15298      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15299      * @param {HTMLElement} node
15300      * @return {HTMLElement} The template node
15301      */
15302     findItemFromChild : function(node){
15303         var el = this.dataName  ?
15304             this.el.child('.roo-tpl-' + this.dataName,true) :
15305             this.el.dom; 
15306         
15307         if(!node || node.parentNode == el){
15308                     return node;
15309             }
15310             var p = node.parentNode;
15311             while(p && p != el){
15312             if(p.parentNode == el){
15313                 return p;
15314             }
15315             p = p.parentNode;
15316         }
15317             return null;
15318     },
15319
15320     /** @ignore */
15321     onClick : function(e){
15322         var item = this.findItemFromChild(e.getTarget());
15323         if(item){
15324             var index = this.indexOf(item);
15325             if(this.onItemClick(item, index, e) !== false){
15326                 this.fireEvent("click", this, index, item, e);
15327             }
15328         }else{
15329             this.clearSelections();
15330         }
15331     },
15332
15333     /** @ignore */
15334     onContextMenu : function(e){
15335         var item = this.findItemFromChild(e.getTarget());
15336         if(item){
15337             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15338         }
15339     },
15340
15341     /** @ignore */
15342     onDblClick : function(e){
15343         var item = this.findItemFromChild(e.getTarget());
15344         if(item){
15345             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15346         }
15347     },
15348
15349     onItemClick : function(item, index, e)
15350     {
15351         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15352             return false;
15353         }
15354         if (this.toggleSelect) {
15355             var m = this.isSelected(item) ? 'unselect' : 'select';
15356             //Roo.log(m);
15357             var _t = this;
15358             _t[m](item, true, false);
15359             return true;
15360         }
15361         if(this.multiSelect || this.singleSelect){
15362             if(this.multiSelect && e.shiftKey && this.lastSelection){
15363                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15364             }else{
15365                 this.select(item, this.multiSelect && e.ctrlKey);
15366                 this.lastSelection = item;
15367             }
15368             
15369             if(!this.tickable){
15370                 e.preventDefault();
15371             }
15372             
15373         }
15374         return true;
15375     },
15376
15377     /**
15378      * Get the number of selected nodes.
15379      * @return {Number}
15380      */
15381     getSelectionCount : function(){
15382         return this.selections.length;
15383     },
15384
15385     /**
15386      * Get the currently selected nodes.
15387      * @return {Array} An array of HTMLElements
15388      */
15389     getSelectedNodes : function(){
15390         return this.selections;
15391     },
15392
15393     /**
15394      * Get the indexes of the selected nodes.
15395      * @return {Array}
15396      */
15397     getSelectedIndexes : function(){
15398         var indexes = [], s = this.selections;
15399         for(var i = 0, len = s.length; i < len; i++){
15400             indexes.push(s[i].nodeIndex);
15401         }
15402         return indexes;
15403     },
15404
15405     /**
15406      * Clear all selections
15407      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15408      */
15409     clearSelections : function(suppressEvent){
15410         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15411             this.cmp.elements = this.selections;
15412             this.cmp.removeClass(this.selectedClass);
15413             this.selections = [];
15414             if(!suppressEvent){
15415                 this.fireEvent("selectionchange", this, this.selections);
15416             }
15417         }
15418     },
15419
15420     /**
15421      * Returns true if the passed node is selected
15422      * @param {HTMLElement/Number} node The node or node index
15423      * @return {Boolean}
15424      */
15425     isSelected : function(node){
15426         var s = this.selections;
15427         if(s.length < 1){
15428             return false;
15429         }
15430         node = this.getNode(node);
15431         return s.indexOf(node) !== -1;
15432     },
15433
15434     /**
15435      * Selects nodes.
15436      * @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
15437      * @param {Boolean} keepExisting (optional) true to keep existing selections
15438      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15439      */
15440     select : function(nodeInfo, keepExisting, suppressEvent){
15441         if(nodeInfo instanceof Array){
15442             if(!keepExisting){
15443                 this.clearSelections(true);
15444             }
15445             for(var i = 0, len = nodeInfo.length; i < len; i++){
15446                 this.select(nodeInfo[i], true, true);
15447             }
15448             return;
15449         } 
15450         var node = this.getNode(nodeInfo);
15451         if(!node || this.isSelected(node)){
15452             return; // already selected.
15453         }
15454         if(!keepExisting){
15455             this.clearSelections(true);
15456         }
15457         
15458         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15459             Roo.fly(node).addClass(this.selectedClass);
15460             this.selections.push(node);
15461             if(!suppressEvent){
15462                 this.fireEvent("selectionchange", this, this.selections);
15463             }
15464         }
15465         
15466         
15467     },
15468       /**
15469      * Unselects nodes.
15470      * @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
15471      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15472      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15473      */
15474     unselect : function(nodeInfo, keepExisting, suppressEvent)
15475     {
15476         if(nodeInfo instanceof Array){
15477             Roo.each(this.selections, function(s) {
15478                 this.unselect(s, nodeInfo);
15479             }, this);
15480             return;
15481         }
15482         var node = this.getNode(nodeInfo);
15483         if(!node || !this.isSelected(node)){
15484             //Roo.log("not selected");
15485             return; // not selected.
15486         }
15487         // fireevent???
15488         var ns = [];
15489         Roo.each(this.selections, function(s) {
15490             if (s == node ) {
15491                 Roo.fly(node).removeClass(this.selectedClass);
15492
15493                 return;
15494             }
15495             ns.push(s);
15496         },this);
15497         
15498         this.selections= ns;
15499         this.fireEvent("selectionchange", this, this.selections);
15500     },
15501
15502     /**
15503      * Gets a template node.
15504      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15505      * @return {HTMLElement} The node or null if it wasn't found
15506      */
15507     getNode : function(nodeInfo){
15508         if(typeof nodeInfo == "string"){
15509             return document.getElementById(nodeInfo);
15510         }else if(typeof nodeInfo == "number"){
15511             return this.nodes[nodeInfo];
15512         }
15513         return nodeInfo;
15514     },
15515
15516     /**
15517      * Gets a range template nodes.
15518      * @param {Number} startIndex
15519      * @param {Number} endIndex
15520      * @return {Array} An array of nodes
15521      */
15522     getNodes : function(start, end){
15523         var ns = this.nodes;
15524         start = start || 0;
15525         end = typeof end == "undefined" ? ns.length - 1 : end;
15526         var nodes = [];
15527         if(start <= end){
15528             for(var i = start; i <= end; i++){
15529                 nodes.push(ns[i]);
15530             }
15531         } else{
15532             for(var i = start; i >= end; i--){
15533                 nodes.push(ns[i]);
15534             }
15535         }
15536         return nodes;
15537     },
15538
15539     /**
15540      * Finds the index of the passed node
15541      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15542      * @return {Number} The index of the node or -1
15543      */
15544     indexOf : function(node){
15545         node = this.getNode(node);
15546         if(typeof node.nodeIndex == "number"){
15547             return node.nodeIndex;
15548         }
15549         var ns = this.nodes;
15550         for(var i = 0, len = ns.length; i < len; i++){
15551             if(ns[i] == node){
15552                 return i;
15553             }
15554         }
15555         return -1;
15556     }
15557 });
15558 /*
15559  * - LGPL
15560  *
15561  * based on jquery fullcalendar
15562  * 
15563  */
15564
15565 Roo.bootstrap = Roo.bootstrap || {};
15566 /**
15567  * @class Roo.bootstrap.Calendar
15568  * @extends Roo.bootstrap.Component
15569  * Bootstrap Calendar class
15570  * @cfg {Boolean} loadMask (true|false) default false
15571  * @cfg {Object} header generate the user specific header of the calendar, default false
15572
15573  * @constructor
15574  * Create a new Container
15575  * @param {Object} config The config object
15576  */
15577
15578
15579
15580 Roo.bootstrap.Calendar = function(config){
15581     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15582      this.addEvents({
15583         /**
15584              * @event select
15585              * Fires when a date is selected
15586              * @param {DatePicker} this
15587              * @param {Date} date The selected date
15588              */
15589         'select': true,
15590         /**
15591              * @event monthchange
15592              * Fires when the displayed month changes 
15593              * @param {DatePicker} this
15594              * @param {Date} date The selected month
15595              */
15596         'monthchange': true,
15597         /**
15598              * @event evententer
15599              * Fires when mouse over an event
15600              * @param {Calendar} this
15601              * @param {event} Event
15602              */
15603         'evententer': true,
15604         /**
15605              * @event eventleave
15606              * Fires when the mouse leaves an
15607              * @param {Calendar} this
15608              * @param {event}
15609              */
15610         'eventleave': true,
15611         /**
15612              * @event eventclick
15613              * Fires when the mouse click an
15614              * @param {Calendar} this
15615              * @param {event}
15616              */
15617         'eventclick': true
15618         
15619     });
15620
15621 };
15622
15623 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
15624     
15625      /**
15626      * @cfg {Number} startDay
15627      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15628      */
15629     startDay : 0,
15630     
15631     loadMask : false,
15632     
15633     header : false,
15634       
15635     getAutoCreate : function(){
15636         
15637         
15638         var fc_button = function(name, corner, style, content ) {
15639             return Roo.apply({},{
15640                 tag : 'span',
15641                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
15642                          (corner.length ?
15643                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15644                             ''
15645                         ),
15646                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15647                 unselectable: 'on'
15648             });
15649         };
15650         
15651         var header = {};
15652         
15653         if(!this.header){
15654             header = {
15655                 tag : 'table',
15656                 cls : 'fc-header',
15657                 style : 'width:100%',
15658                 cn : [
15659                     {
15660                         tag: 'tr',
15661                         cn : [
15662                             {
15663                                 tag : 'td',
15664                                 cls : 'fc-header-left',
15665                                 cn : [
15666                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
15667                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
15668                                     { tag: 'span', cls: 'fc-header-space' },
15669                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
15670
15671
15672                                 ]
15673                             },
15674
15675                             {
15676                                 tag : 'td',
15677                                 cls : 'fc-header-center',
15678                                 cn : [
15679                                     {
15680                                         tag: 'span',
15681                                         cls: 'fc-header-title',
15682                                         cn : {
15683                                             tag: 'H2',
15684                                             html : 'month / year'
15685                                         }
15686                                     }
15687
15688                                 ]
15689                             },
15690                             {
15691                                 tag : 'td',
15692                                 cls : 'fc-header-right',
15693                                 cn : [
15694                               /*      fc_button('month', 'left', '', 'month' ),
15695                                     fc_button('week', '', '', 'week' ),
15696                                     fc_button('day', 'right', '', 'day' )
15697                                 */    
15698
15699                                 ]
15700                             }
15701
15702                         ]
15703                     }
15704                 ]
15705             };
15706         }
15707         
15708         header = this.header;
15709         
15710        
15711         var cal_heads = function() {
15712             var ret = [];
15713             // fixme - handle this.
15714             
15715             for (var i =0; i < Date.dayNames.length; i++) {
15716                 var d = Date.dayNames[i];
15717                 ret.push({
15718                     tag: 'th',
15719                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15720                     html : d.substring(0,3)
15721                 });
15722                 
15723             }
15724             ret[0].cls += ' fc-first';
15725             ret[6].cls += ' fc-last';
15726             return ret;
15727         };
15728         var cal_cell = function(n) {
15729             return  {
15730                 tag: 'td',
15731                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15732                 cn : [
15733                     {
15734                         cn : [
15735                             {
15736                                 cls: 'fc-day-number',
15737                                 html: 'D'
15738                             },
15739                             {
15740                                 cls: 'fc-day-content',
15741                              
15742                                 cn : [
15743                                      {
15744                                         style: 'position: relative;' // height: 17px;
15745                                     }
15746                                 ]
15747                             }
15748                             
15749                             
15750                         ]
15751                     }
15752                 ]
15753                 
15754             }
15755         };
15756         var cal_rows = function() {
15757             
15758             var ret = [];
15759             for (var r = 0; r < 6; r++) {
15760                 var row= {
15761                     tag : 'tr',
15762                     cls : 'fc-week',
15763                     cn : []
15764                 };
15765                 
15766                 for (var i =0; i < Date.dayNames.length; i++) {
15767                     var d = Date.dayNames[i];
15768                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15769
15770                 }
15771                 row.cn[0].cls+=' fc-first';
15772                 row.cn[0].cn[0].style = 'min-height:90px';
15773                 row.cn[6].cls+=' fc-last';
15774                 ret.push(row);
15775                 
15776             }
15777             ret[0].cls += ' fc-first';
15778             ret[4].cls += ' fc-prev-last';
15779             ret[5].cls += ' fc-last';
15780             return ret;
15781             
15782         };
15783         
15784         var cal_table = {
15785             tag: 'table',
15786             cls: 'fc-border-separate',
15787             style : 'width:100%',
15788             cellspacing  : 0,
15789             cn : [
15790                 { 
15791                     tag: 'thead',
15792                     cn : [
15793                         { 
15794                             tag: 'tr',
15795                             cls : 'fc-first fc-last',
15796                             cn : cal_heads()
15797                         }
15798                     ]
15799                 },
15800                 { 
15801                     tag: 'tbody',
15802                     cn : cal_rows()
15803                 }
15804                   
15805             ]
15806         };
15807          
15808          var cfg = {
15809             cls : 'fc fc-ltr',
15810             cn : [
15811                 header,
15812                 {
15813                     cls : 'fc-content',
15814                     style : "position: relative;",
15815                     cn : [
15816                         {
15817                             cls : 'fc-view fc-view-month fc-grid',
15818                             style : 'position: relative',
15819                             unselectable : 'on',
15820                             cn : [
15821                                 {
15822                                     cls : 'fc-event-container',
15823                                     style : 'position:absolute;z-index:8;top:0;left:0;'
15824                                 },
15825                                 cal_table
15826                             ]
15827                         }
15828                     ]
15829     
15830                 }
15831            ] 
15832             
15833         };
15834         
15835          
15836         
15837         return cfg;
15838     },
15839     
15840     
15841     initEvents : function()
15842     {
15843         if(!this.store){
15844             throw "can not find store for calendar";
15845         }
15846         
15847         var mark = {
15848             tag: "div",
15849             cls:"x-dlg-mask",
15850             style: "text-align:center",
15851             cn: [
15852                 {
15853                     tag: "div",
15854                     style: "background-color:white;width:50%;margin:250 auto",
15855                     cn: [
15856                         {
15857                             tag: "img",
15858                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15859                         },
15860                         {
15861                             tag: "span",
15862                             html: "Loading"
15863                         }
15864                         
15865                     ]
15866                 }
15867             ]
15868         };
15869         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15870         
15871         var size = this.el.select('.fc-content', true).first().getSize();
15872         this.maskEl.setSize(size.width, size.height);
15873         this.maskEl.enableDisplayMode("block");
15874         if(!this.loadMask){
15875             this.maskEl.hide();
15876         }
15877         
15878         this.store = Roo.factory(this.store, Roo.data);
15879         this.store.on('load', this.onLoad, this);
15880         this.store.on('beforeload', this.onBeforeLoad, this);
15881         
15882         this.resize();
15883         
15884         this.cells = this.el.select('.fc-day',true);
15885         //Roo.log(this.cells);
15886         this.textNodes = this.el.query('.fc-day-number');
15887         this.cells.addClassOnOver('fc-state-hover');
15888         
15889         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15890         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15891         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15892         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15893         
15894         this.on('monthchange', this.onMonthChange, this);
15895         
15896         this.update(new Date().clearTime());
15897     },
15898     
15899     resize : function() {
15900         var sz  = this.el.getSize();
15901         
15902         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15903         this.el.select('.fc-day-content div',true).setHeight(34);
15904     },
15905     
15906     
15907     // private
15908     showPrevMonth : function(e){
15909         this.update(this.activeDate.add("mo", -1));
15910     },
15911     showToday : function(e){
15912         this.update(new Date().clearTime());
15913     },
15914     // private
15915     showNextMonth : function(e){
15916         this.update(this.activeDate.add("mo", 1));
15917     },
15918
15919     // private
15920     showPrevYear : function(){
15921         this.update(this.activeDate.add("y", -1));
15922     },
15923
15924     // private
15925     showNextYear : function(){
15926         this.update(this.activeDate.add("y", 1));
15927     },
15928
15929     
15930    // private
15931     update : function(date)
15932     {
15933         var vd = this.activeDate;
15934         this.activeDate = date;
15935 //        if(vd && this.el){
15936 //            var t = date.getTime();
15937 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15938 //                Roo.log('using add remove');
15939 //                
15940 //                this.fireEvent('monthchange', this, date);
15941 //                
15942 //                this.cells.removeClass("fc-state-highlight");
15943 //                this.cells.each(function(c){
15944 //                   if(c.dateValue == t){
15945 //                       c.addClass("fc-state-highlight");
15946 //                       setTimeout(function(){
15947 //                            try{c.dom.firstChild.focus();}catch(e){}
15948 //                       }, 50);
15949 //                       return false;
15950 //                   }
15951 //                   return true;
15952 //                });
15953 //                return;
15954 //            }
15955 //        }
15956         
15957         var days = date.getDaysInMonth();
15958         
15959         var firstOfMonth = date.getFirstDateOfMonth();
15960         var startingPos = firstOfMonth.getDay()-this.startDay;
15961         
15962         if(startingPos < this.startDay){
15963             startingPos += 7;
15964         }
15965         
15966         var pm = date.add(Date.MONTH, -1);
15967         var prevStart = pm.getDaysInMonth()-startingPos;
15968 //        
15969         this.cells = this.el.select('.fc-day',true);
15970         this.textNodes = this.el.query('.fc-day-number');
15971         this.cells.addClassOnOver('fc-state-hover');
15972         
15973         var cells = this.cells.elements;
15974         var textEls = this.textNodes;
15975         
15976         Roo.each(cells, function(cell){
15977             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15978         });
15979         
15980         days += startingPos;
15981
15982         // convert everything to numbers so it's fast
15983         var day = 86400000;
15984         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15985         //Roo.log(d);
15986         //Roo.log(pm);
15987         //Roo.log(prevStart);
15988         
15989         var today = new Date().clearTime().getTime();
15990         var sel = date.clearTime().getTime();
15991         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15992         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15993         var ddMatch = this.disabledDatesRE;
15994         var ddText = this.disabledDatesText;
15995         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15996         var ddaysText = this.disabledDaysText;
15997         var format = this.format;
15998         
15999         var setCellClass = function(cal, cell){
16000             cell.row = 0;
16001             cell.events = [];
16002             cell.more = [];
16003             //Roo.log('set Cell Class');
16004             cell.title = "";
16005             var t = d.getTime();
16006             
16007             //Roo.log(d);
16008             
16009             cell.dateValue = t;
16010             if(t == today){
16011                 cell.className += " fc-today";
16012                 cell.className += " fc-state-highlight";
16013                 cell.title = cal.todayText;
16014             }
16015             if(t == sel){
16016                 // disable highlight in other month..
16017                 //cell.className += " fc-state-highlight";
16018                 
16019             }
16020             // disabling
16021             if(t < min) {
16022                 cell.className = " fc-state-disabled";
16023                 cell.title = cal.minText;
16024                 return;
16025             }
16026             if(t > max) {
16027                 cell.className = " fc-state-disabled";
16028                 cell.title = cal.maxText;
16029                 return;
16030             }
16031             if(ddays){
16032                 if(ddays.indexOf(d.getDay()) != -1){
16033                     cell.title = ddaysText;
16034                     cell.className = " fc-state-disabled";
16035                 }
16036             }
16037             if(ddMatch && format){
16038                 var fvalue = d.dateFormat(format);
16039                 if(ddMatch.test(fvalue)){
16040                     cell.title = ddText.replace("%0", fvalue);
16041                     cell.className = " fc-state-disabled";
16042                 }
16043             }
16044             
16045             if (!cell.initialClassName) {
16046                 cell.initialClassName = cell.dom.className;
16047             }
16048             
16049             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16050         };
16051
16052         var i = 0;
16053         
16054         for(; i < startingPos; i++) {
16055             textEls[i].innerHTML = (++prevStart);
16056             d.setDate(d.getDate()+1);
16057             
16058             cells[i].className = "fc-past fc-other-month";
16059             setCellClass(this, cells[i]);
16060         }
16061         
16062         var intDay = 0;
16063         
16064         for(; i < days; i++){
16065             intDay = i - startingPos + 1;
16066             textEls[i].innerHTML = (intDay);
16067             d.setDate(d.getDate()+1);
16068             
16069             cells[i].className = ''; // "x-date-active";
16070             setCellClass(this, cells[i]);
16071         }
16072         var extraDays = 0;
16073         
16074         for(; i < 42; i++) {
16075             textEls[i].innerHTML = (++extraDays);
16076             d.setDate(d.getDate()+1);
16077             
16078             cells[i].className = "fc-future fc-other-month";
16079             setCellClass(this, cells[i]);
16080         }
16081         
16082         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16083         
16084         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16085         
16086         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16087         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16088         
16089         if(totalRows != 6){
16090             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16091             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16092         }
16093         
16094         this.fireEvent('monthchange', this, date);
16095         
16096         
16097         /*
16098         if(!this.internalRender){
16099             var main = this.el.dom.firstChild;
16100             var w = main.offsetWidth;
16101             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16102             Roo.fly(main).setWidth(w);
16103             this.internalRender = true;
16104             // opera does not respect the auto grow header center column
16105             // then, after it gets a width opera refuses to recalculate
16106             // without a second pass
16107             if(Roo.isOpera && !this.secondPass){
16108                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16109                 this.secondPass = true;
16110                 this.update.defer(10, this, [date]);
16111             }
16112         }
16113         */
16114         
16115     },
16116     
16117     findCell : function(dt) {
16118         dt = dt.clearTime().getTime();
16119         var ret = false;
16120         this.cells.each(function(c){
16121             //Roo.log("check " +c.dateValue + '?=' + dt);
16122             if(c.dateValue == dt){
16123                 ret = c;
16124                 return false;
16125             }
16126             return true;
16127         });
16128         
16129         return ret;
16130     },
16131     
16132     findCells : function(ev) {
16133         var s = ev.start.clone().clearTime().getTime();
16134        // Roo.log(s);
16135         var e= ev.end.clone().clearTime().getTime();
16136        // Roo.log(e);
16137         var ret = [];
16138         this.cells.each(function(c){
16139              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16140             
16141             if(c.dateValue > e){
16142                 return ;
16143             }
16144             if(c.dateValue < s){
16145                 return ;
16146             }
16147             ret.push(c);
16148         });
16149         
16150         return ret;    
16151     },
16152     
16153 //    findBestRow: function(cells)
16154 //    {
16155 //        var ret = 0;
16156 //        
16157 //        for (var i =0 ; i < cells.length;i++) {
16158 //            ret  = Math.max(cells[i].rows || 0,ret);
16159 //        }
16160 //        return ret;
16161 //        
16162 //    },
16163     
16164     
16165     addItem : function(ev)
16166     {
16167         // look for vertical location slot in
16168         var cells = this.findCells(ev);
16169         
16170 //        ev.row = this.findBestRow(cells);
16171         
16172         // work out the location.
16173         
16174         var crow = false;
16175         var rows = [];
16176         for(var i =0; i < cells.length; i++) {
16177             
16178             cells[i].row = cells[0].row;
16179             
16180             if(i == 0){
16181                 cells[i].row = cells[i].row + 1;
16182             }
16183             
16184             if (!crow) {
16185                 crow = {
16186                     start : cells[i],
16187                     end :  cells[i]
16188                 };
16189                 continue;
16190             }
16191             if (crow.start.getY() == cells[i].getY()) {
16192                 // on same row.
16193                 crow.end = cells[i];
16194                 continue;
16195             }
16196             // different row.
16197             rows.push(crow);
16198             crow = {
16199                 start: cells[i],
16200                 end : cells[i]
16201             };
16202             
16203         }
16204         
16205         rows.push(crow);
16206         ev.els = [];
16207         ev.rows = rows;
16208         ev.cells = cells;
16209         
16210         cells[0].events.push(ev);
16211         
16212         this.calevents.push(ev);
16213     },
16214     
16215     clearEvents: function() {
16216         
16217         if(!this.calevents){
16218             return;
16219         }
16220         
16221         Roo.each(this.cells.elements, function(c){
16222             c.row = 0;
16223             c.events = [];
16224             c.more = [];
16225         });
16226         
16227         Roo.each(this.calevents, function(e) {
16228             Roo.each(e.els, function(el) {
16229                 el.un('mouseenter' ,this.onEventEnter, this);
16230                 el.un('mouseleave' ,this.onEventLeave, this);
16231                 el.remove();
16232             },this);
16233         },this);
16234         
16235         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16236             e.remove();
16237         });
16238         
16239     },
16240     
16241     renderEvents: function()
16242     {   
16243         var _this = this;
16244         
16245         this.cells.each(function(c) {
16246             
16247             if(c.row < 5){
16248                 return;
16249             }
16250             
16251             var ev = c.events;
16252             
16253             var r = 4;
16254             if(c.row != c.events.length){
16255                 r = 4 - (4 - (c.row - c.events.length));
16256             }
16257             
16258             c.events = ev.slice(0, r);
16259             c.more = ev.slice(r);
16260             
16261             if(c.more.length && c.more.length == 1){
16262                 c.events.push(c.more.pop());
16263             }
16264             
16265             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16266             
16267         });
16268             
16269         this.cells.each(function(c) {
16270             
16271             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16272             
16273             
16274             for (var e = 0; e < c.events.length; e++){
16275                 var ev = c.events[e];
16276                 var rows = ev.rows;
16277                 
16278                 for(var i = 0; i < rows.length; i++) {
16279                 
16280                     // how many rows should it span..
16281
16282                     var  cfg = {
16283                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16284                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16285
16286                         unselectable : "on",
16287                         cn : [
16288                             {
16289                                 cls: 'fc-event-inner',
16290                                 cn : [
16291     //                                {
16292     //                                  tag:'span',
16293     //                                  cls: 'fc-event-time',
16294     //                                  html : cells.length > 1 ? '' : ev.time
16295     //                                },
16296                                     {
16297                                       tag:'span',
16298                                       cls: 'fc-event-title',
16299                                       html : String.format('{0}', ev.title)
16300                                     }
16301
16302
16303                                 ]
16304                             },
16305                             {
16306                                 cls: 'ui-resizable-handle ui-resizable-e',
16307                                 html : '&nbsp;&nbsp;&nbsp'
16308                             }
16309
16310                         ]
16311                     };
16312
16313                     if (i == 0) {
16314                         cfg.cls += ' fc-event-start';
16315                     }
16316                     if ((i+1) == rows.length) {
16317                         cfg.cls += ' fc-event-end';
16318                     }
16319
16320                     var ctr = _this.el.select('.fc-event-container',true).first();
16321                     var cg = ctr.createChild(cfg);
16322
16323                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16324                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16325
16326                     var r = (c.more.length) ? 1 : 0;
16327                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16328                     cg.setWidth(ebox.right - sbox.x -2);
16329
16330                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16331                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16332                     cg.on('click', _this.onEventClick, _this, ev);
16333
16334                     ev.els.push(cg);
16335                     
16336                 }
16337                 
16338             }
16339             
16340             
16341             if(c.more.length){
16342                 var  cfg = {
16343                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16344                     style : 'position: absolute',
16345                     unselectable : "on",
16346                     cn : [
16347                         {
16348                             cls: 'fc-event-inner',
16349                             cn : [
16350                                 {
16351                                   tag:'span',
16352                                   cls: 'fc-event-title',
16353                                   html : 'More'
16354                                 }
16355
16356
16357                             ]
16358                         },
16359                         {
16360                             cls: 'ui-resizable-handle ui-resizable-e',
16361                             html : '&nbsp;&nbsp;&nbsp'
16362                         }
16363
16364                     ]
16365                 };
16366
16367                 var ctr = _this.el.select('.fc-event-container',true).first();
16368                 var cg = ctr.createChild(cfg);
16369
16370                 var sbox = c.select('.fc-day-content',true).first().getBox();
16371                 var ebox = c.select('.fc-day-content',true).first().getBox();
16372                 //Roo.log(cg);
16373                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16374                 cg.setWidth(ebox.right - sbox.x -2);
16375
16376                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16377                 
16378             }
16379             
16380         });
16381         
16382         
16383         
16384     },
16385     
16386     onEventEnter: function (e, el,event,d) {
16387         this.fireEvent('evententer', this, el, event);
16388     },
16389     
16390     onEventLeave: function (e, el,event,d) {
16391         this.fireEvent('eventleave', this, el, event);
16392     },
16393     
16394     onEventClick: function (e, el,event,d) {
16395         this.fireEvent('eventclick', this, el, event);
16396     },
16397     
16398     onMonthChange: function () {
16399         this.store.load();
16400     },
16401     
16402     onMoreEventClick: function(e, el, more)
16403     {
16404         var _this = this;
16405         
16406         this.calpopover.placement = 'right';
16407         this.calpopover.setTitle('More');
16408         
16409         this.calpopover.setContent('');
16410         
16411         var ctr = this.calpopover.el.select('.popover-content', true).first();
16412         
16413         Roo.each(more, function(m){
16414             var cfg = {
16415                 cls : 'fc-event-hori fc-event-draggable',
16416                 html : m.title
16417             };
16418             var cg = ctr.createChild(cfg);
16419             
16420             cg.on('click', _this.onEventClick, _this, m);
16421         });
16422         
16423         this.calpopover.show(el);
16424         
16425         
16426     },
16427     
16428     onLoad: function () 
16429     {   
16430         this.calevents = [];
16431         var cal = this;
16432         
16433         if(this.store.getCount() > 0){
16434             this.store.data.each(function(d){
16435                cal.addItem({
16436                     id : d.data.id,
16437                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16438                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16439                     time : d.data.start_time,
16440                     title : d.data.title,
16441                     description : d.data.description,
16442                     venue : d.data.venue
16443                 });
16444             });
16445         }
16446         
16447         this.renderEvents();
16448         
16449         if(this.calevents.length && this.loadMask){
16450             this.maskEl.hide();
16451         }
16452     },
16453     
16454     onBeforeLoad: function()
16455     {
16456         this.clearEvents();
16457         if(this.loadMask){
16458             this.maskEl.show();
16459         }
16460     }
16461 });
16462
16463  
16464  /*
16465  * - LGPL
16466  *
16467  * element
16468  * 
16469  */
16470
16471 /**
16472  * @class Roo.bootstrap.Popover
16473  * @extends Roo.bootstrap.Component
16474  * Bootstrap Popover class
16475  * @cfg {String} html contents of the popover   (or false to use children..)
16476  * @cfg {String} title of popover (or false to hide)
16477  * @cfg {String} placement how it is placed
16478  * @cfg {String} trigger click || hover (or false to trigger manually)
16479  * @cfg {String} over what (parent or false to trigger manually.)
16480  * @cfg {Number} delay - delay before showing
16481  
16482  * @constructor
16483  * Create a new Popover
16484  * @param {Object} config The config object
16485  */
16486
16487 Roo.bootstrap.Popover = function(config){
16488     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16489     
16490     this.addEvents({
16491         // raw events
16492          /**
16493          * @event show
16494          * After the popover show
16495          * 
16496          * @param {Roo.bootstrap.Popover} this
16497          */
16498         "show" : true,
16499         /**
16500          * @event hide
16501          * After the popover hide
16502          * 
16503          * @param {Roo.bootstrap.Popover} this
16504          */
16505         "hide" : true
16506     });
16507 };
16508
16509 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
16510     
16511     title: 'Fill in a title',
16512     html: false,
16513     
16514     placement : 'right',
16515     trigger : 'hover', // hover
16516     
16517     delay : 0,
16518     
16519     over: 'parent',
16520     
16521     can_build_overlaid : false,
16522     
16523     getChildContainer : function()
16524     {
16525         return this.el.select('.popover-content',true).first();
16526     },
16527     
16528     getAutoCreate : function(){
16529          
16530         var cfg = {
16531            cls : 'popover roo-dynamic',
16532            style: 'display:block',
16533            cn : [
16534                 {
16535                     cls : 'arrow'
16536                 },
16537                 {
16538                     cls : 'popover-inner',
16539                     cn : [
16540                         {
16541                             tag: 'h3',
16542                             cls: 'popover-title',
16543                             html : this.title
16544                         },
16545                         {
16546                             cls : 'popover-content',
16547                             html : this.html
16548                         }
16549                     ]
16550                     
16551                 }
16552            ]
16553         };
16554         
16555         return cfg;
16556     },
16557     setTitle: function(str)
16558     {
16559         this.title = str;
16560         this.el.select('.popover-title',true).first().dom.innerHTML = str;
16561     },
16562     setContent: function(str)
16563     {
16564         this.html = str;
16565         this.el.select('.popover-content',true).first().dom.innerHTML = str;
16566     },
16567     // as it get's added to the bottom of the page.
16568     onRender : function(ct, position)
16569     {
16570         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16571         if(!this.el){
16572             var cfg = Roo.apply({},  this.getAutoCreate());
16573             cfg.id = Roo.id();
16574             
16575             if (this.cls) {
16576                 cfg.cls += ' ' + this.cls;
16577             }
16578             if (this.style) {
16579                 cfg.style = this.style;
16580             }
16581             //Roo.log("adding to ");
16582             this.el = Roo.get(document.body).createChild(cfg, position);
16583 //            Roo.log(this.el);
16584         }
16585         this.initEvents();
16586     },
16587     
16588     initEvents : function()
16589     {
16590         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16591         this.el.enableDisplayMode('block');
16592         this.el.hide();
16593         if (this.over === false) {
16594             return; 
16595         }
16596         if (this.triggers === false) {
16597             return;
16598         }
16599         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16600         var triggers = this.trigger ? this.trigger.split(' ') : [];
16601         Roo.each(triggers, function(trigger) {
16602         
16603             if (trigger == 'click') {
16604                 on_el.on('click', this.toggle, this);
16605             } else if (trigger != 'manual') {
16606                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
16607                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16608       
16609                 on_el.on(eventIn  ,this.enter, this);
16610                 on_el.on(eventOut, this.leave, this);
16611             }
16612         }, this);
16613         
16614     },
16615     
16616     
16617     // private
16618     timeout : null,
16619     hoverState : null,
16620     
16621     toggle : function () {
16622         this.hoverState == 'in' ? this.leave() : this.enter();
16623     },
16624     
16625     enter : function () {
16626         
16627         clearTimeout(this.timeout);
16628     
16629         this.hoverState = 'in';
16630     
16631         if (!this.delay || !this.delay.show) {
16632             this.show();
16633             return;
16634         }
16635         var _t = this;
16636         this.timeout = setTimeout(function () {
16637             if (_t.hoverState == 'in') {
16638                 _t.show();
16639             }
16640         }, this.delay.show)
16641     },
16642     
16643     leave : function() {
16644         clearTimeout(this.timeout);
16645     
16646         this.hoverState = 'out';
16647     
16648         if (!this.delay || !this.delay.hide) {
16649             this.hide();
16650             return;
16651         }
16652         var _t = this;
16653         this.timeout = setTimeout(function () {
16654             if (_t.hoverState == 'out') {
16655                 _t.hide();
16656             }
16657         }, this.delay.hide)
16658     },
16659     
16660     show : function (on_el)
16661     {
16662         if (!on_el) {
16663             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16664         }
16665         
16666         // set content.
16667         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16668         if (this.html !== false) {
16669             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16670         }
16671         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16672         if (!this.title.length) {
16673             this.el.select('.popover-title',true).hide();
16674         }
16675         
16676         var placement = typeof this.placement == 'function' ?
16677             this.placement.call(this, this.el, on_el) :
16678             this.placement;
16679             
16680         var autoToken = /\s?auto?\s?/i;
16681         var autoPlace = autoToken.test(placement);
16682         if (autoPlace) {
16683             placement = placement.replace(autoToken, '') || 'top';
16684         }
16685         
16686         //this.el.detach()
16687         //this.el.setXY([0,0]);
16688         this.el.show();
16689         this.el.dom.style.display='block';
16690         this.el.addClass(placement);
16691         
16692         //this.el.appendTo(on_el);
16693         
16694         var p = this.getPosition();
16695         var box = this.el.getBox();
16696         
16697         if (autoPlace) {
16698             // fixme..
16699         }
16700         var align = Roo.bootstrap.Popover.alignment[placement];
16701         this.el.alignTo(on_el, align[0],align[1]);
16702         //var arrow = this.el.select('.arrow',true).first();
16703         //arrow.set(align[2], 
16704         
16705         this.el.addClass('in');
16706         
16707         
16708         if (this.el.hasClass('fade')) {
16709             // fade it?
16710         }
16711         
16712         this.hoverState = 'in';
16713         
16714         this.fireEvent('show', this);
16715         
16716     },
16717     hide : function()
16718     {
16719         this.el.setXY([0,0]);
16720         this.el.removeClass('in');
16721         this.el.hide();
16722         this.hoverState = null;
16723         
16724         this.fireEvent('hide', this);
16725     }
16726     
16727 });
16728
16729 Roo.bootstrap.Popover.alignment = {
16730     'left' : ['r-l', [-10,0], 'right'],
16731     'right' : ['l-r', [10,0], 'left'],
16732     'bottom' : ['t-b', [0,10], 'top'],
16733     'top' : [ 'b-t', [0,-10], 'bottom']
16734 };
16735
16736  /*
16737  * - LGPL
16738  *
16739  * Progress
16740  * 
16741  */
16742
16743 /**
16744  * @class Roo.bootstrap.Progress
16745  * @extends Roo.bootstrap.Component
16746  * Bootstrap Progress class
16747  * @cfg {Boolean} striped striped of the progress bar
16748  * @cfg {Boolean} active animated of the progress bar
16749  * 
16750  * 
16751  * @constructor
16752  * Create a new Progress
16753  * @param {Object} config The config object
16754  */
16755
16756 Roo.bootstrap.Progress = function(config){
16757     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16758 };
16759
16760 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
16761     
16762     striped : false,
16763     active: false,
16764     
16765     getAutoCreate : function(){
16766         var cfg = {
16767             tag: 'div',
16768             cls: 'progress'
16769         };
16770         
16771         
16772         if(this.striped){
16773             cfg.cls += ' progress-striped';
16774         }
16775       
16776         if(this.active){
16777             cfg.cls += ' active';
16778         }
16779         
16780         
16781         return cfg;
16782     }
16783    
16784 });
16785
16786  
16787
16788  /*
16789  * - LGPL
16790  *
16791  * ProgressBar
16792  * 
16793  */
16794
16795 /**
16796  * @class Roo.bootstrap.ProgressBar
16797  * @extends Roo.bootstrap.Component
16798  * Bootstrap ProgressBar class
16799  * @cfg {Number} aria_valuenow aria-value now
16800  * @cfg {Number} aria_valuemin aria-value min
16801  * @cfg {Number} aria_valuemax aria-value max
16802  * @cfg {String} label label for the progress bar
16803  * @cfg {String} panel (success | info | warning | danger )
16804  * @cfg {String} role role of the progress bar
16805  * @cfg {String} sr_only text
16806  * 
16807  * 
16808  * @constructor
16809  * Create a new ProgressBar
16810  * @param {Object} config The config object
16811  */
16812
16813 Roo.bootstrap.ProgressBar = function(config){
16814     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16815 };
16816
16817 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
16818     
16819     aria_valuenow : 0,
16820     aria_valuemin : 0,
16821     aria_valuemax : 100,
16822     label : false,
16823     panel : false,
16824     role : false,
16825     sr_only: false,
16826     
16827     getAutoCreate : function()
16828     {
16829         
16830         var cfg = {
16831             tag: 'div',
16832             cls: 'progress-bar',
16833             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16834         };
16835         
16836         if(this.sr_only){
16837             cfg.cn = {
16838                 tag: 'span',
16839                 cls: 'sr-only',
16840                 html: this.sr_only
16841             }
16842         }
16843         
16844         if(this.role){
16845             cfg.role = this.role;
16846         }
16847         
16848         if(this.aria_valuenow){
16849             cfg['aria-valuenow'] = this.aria_valuenow;
16850         }
16851         
16852         if(this.aria_valuemin){
16853             cfg['aria-valuemin'] = this.aria_valuemin;
16854         }
16855         
16856         if(this.aria_valuemax){
16857             cfg['aria-valuemax'] = this.aria_valuemax;
16858         }
16859         
16860         if(this.label && !this.sr_only){
16861             cfg.html = this.label;
16862         }
16863         
16864         if(this.panel){
16865             cfg.cls += ' progress-bar-' + this.panel;
16866         }
16867         
16868         return cfg;
16869     },
16870     
16871     update : function(aria_valuenow)
16872     {
16873         this.aria_valuenow = aria_valuenow;
16874         
16875         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16876     }
16877    
16878 });
16879
16880  
16881
16882  /*
16883  * - LGPL
16884  *
16885  * column
16886  * 
16887  */
16888
16889 /**
16890  * @class Roo.bootstrap.TabGroup
16891  * @extends Roo.bootstrap.Column
16892  * Bootstrap Column class
16893  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16894  * @cfg {Boolean} carousel true to make the group behave like a carousel
16895  * @cfg {Boolean} bullets show bullets for the panels
16896  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16897  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16898  * @cfg {Boolean} showarrow (true|false) show arrow default true
16899  * 
16900  * @constructor
16901  * Create a new TabGroup
16902  * @param {Object} config The config object
16903  */
16904
16905 Roo.bootstrap.TabGroup = function(config){
16906     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16907     if (!this.navId) {
16908         this.navId = Roo.id();
16909     }
16910     this.tabs = [];
16911     Roo.bootstrap.TabGroup.register(this);
16912     
16913 };
16914
16915 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16916     
16917     carousel : false,
16918     transition : false,
16919     bullets : 0,
16920     timer : 0,
16921     autoslide : false,
16922     slideFn : false,
16923     slideOnTouch : false,
16924     showarrow : true,
16925     
16926     getAutoCreate : function()
16927     {
16928         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16929         
16930         cfg.cls += ' tab-content';
16931         
16932         if (this.carousel) {
16933             cfg.cls += ' carousel slide';
16934             
16935             cfg.cn = [{
16936                cls : 'carousel-inner',
16937                cn : []
16938             }];
16939         
16940             if(this.bullets  && !Roo.isTouch){
16941                 
16942                 var bullets = {
16943                     cls : 'carousel-bullets',
16944                     cn : []
16945                 };
16946                
16947                 if(this.bullets_cls){
16948                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16949                 }
16950                 
16951                 bullets.cn.push({
16952                     cls : 'clear'
16953                 });
16954                 
16955                 cfg.cn[0].cn.push(bullets);
16956             }
16957             
16958             if(this.showarrow){
16959                 cfg.cn[0].cn.push({
16960                     tag : 'div',
16961                     class : 'carousel-arrow',
16962                     cn : [
16963                         {
16964                             tag : 'div',
16965                             class : 'carousel-prev',
16966                             cn : [
16967                                 {
16968                                     tag : 'i',
16969                                     class : 'fa fa-chevron-left'
16970                                 }
16971                             ]
16972                         },
16973                         {
16974                             tag : 'div',
16975                             class : 'carousel-next',
16976                             cn : [
16977                                 {
16978                                     tag : 'i',
16979                                     class : 'fa fa-chevron-right'
16980                                 }
16981                             ]
16982                         }
16983                     ]
16984                 });
16985             }
16986             
16987         }
16988         
16989         return cfg;
16990     },
16991     
16992     initEvents:  function()
16993     {
16994 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
16995 //            this.el.on("touchstart", this.onTouchStart, this);
16996 //        }
16997         
16998         if(this.autoslide){
16999             var _this = this;
17000             
17001             this.slideFn = window.setInterval(function() {
17002                 _this.showPanelNext();
17003             }, this.timer);
17004         }
17005         
17006         if(this.showarrow){
17007             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17008             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17009         }
17010         
17011         
17012     },
17013     
17014 //    onTouchStart : function(e, el, o)
17015 //    {
17016 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17017 //            return;
17018 //        }
17019 //        
17020 //        this.showPanelNext();
17021 //    },
17022     
17023     
17024     getChildContainer : function()
17025     {
17026         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17027     },
17028     
17029     /**
17030     * register a Navigation item
17031     * @param {Roo.bootstrap.NavItem} the navitem to add
17032     */
17033     register : function(item)
17034     {
17035         this.tabs.push( item);
17036         item.navId = this.navId; // not really needed..
17037         this.addBullet();
17038     
17039     },
17040     
17041     getActivePanel : function()
17042     {
17043         var r = false;
17044         Roo.each(this.tabs, function(t) {
17045             if (t.active) {
17046                 r = t;
17047                 return false;
17048             }
17049             return null;
17050         });
17051         return r;
17052         
17053     },
17054     getPanelByName : function(n)
17055     {
17056         var r = false;
17057         Roo.each(this.tabs, function(t) {
17058             if (t.tabId == n) {
17059                 r = t;
17060                 return false;
17061             }
17062             return null;
17063         });
17064         return r;
17065     },
17066     indexOfPanel : function(p)
17067     {
17068         var r = false;
17069         Roo.each(this.tabs, function(t,i) {
17070             if (t.tabId == p.tabId) {
17071                 r = i;
17072                 return false;
17073             }
17074             return null;
17075         });
17076         return r;
17077     },
17078     /**
17079      * show a specific panel
17080      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17081      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17082      */
17083     showPanel : function (pan)
17084     {
17085         if(this.transition || typeof(pan) == 'undefined'){
17086             Roo.log("waiting for the transitionend");
17087             return;
17088         }
17089         
17090         if (typeof(pan) == 'number') {
17091             pan = this.tabs[pan];
17092         }
17093         
17094         if (typeof(pan) == 'string') {
17095             pan = this.getPanelByName(pan);
17096         }
17097         
17098         var cur = this.getActivePanel();
17099         
17100         if(!pan || !cur){
17101             Roo.log('pan or acitve pan is undefined');
17102             return false;
17103         }
17104         
17105         if (pan.tabId == this.getActivePanel().tabId) {
17106             return true;
17107         }
17108         
17109         if (false === cur.fireEvent('beforedeactivate')) {
17110             return false;
17111         }
17112         
17113         if(this.bullets > 0 && !Roo.isTouch){
17114             this.setActiveBullet(this.indexOfPanel(pan));
17115         }
17116         
17117         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17118             
17119             this.transition = true;
17120             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17121             var lr = dir == 'next' ? 'left' : 'right';
17122             pan.el.addClass(dir); // or prev
17123             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17124             cur.el.addClass(lr); // or right
17125             pan.el.addClass(lr);
17126             
17127             var _this = this;
17128             cur.el.on('transitionend', function() {
17129                 Roo.log("trans end?");
17130                 
17131                 pan.el.removeClass([lr,dir]);
17132                 pan.setActive(true);
17133                 
17134                 cur.el.removeClass([lr]);
17135                 cur.setActive(false);
17136                 
17137                 _this.transition = false;
17138                 
17139             }, this, { single:  true } );
17140             
17141             return true;
17142         }
17143         
17144         cur.setActive(false);
17145         pan.setActive(true);
17146         
17147         return true;
17148         
17149     },
17150     showPanelNext : function()
17151     {
17152         var i = this.indexOfPanel(this.getActivePanel());
17153         
17154         if (i >= this.tabs.length - 1 && !this.autoslide) {
17155             return;
17156         }
17157         
17158         if (i >= this.tabs.length - 1 && this.autoslide) {
17159             i = -1;
17160         }
17161         
17162         this.showPanel(this.tabs[i+1]);
17163     },
17164     
17165     showPanelPrev : function()
17166     {
17167         var i = this.indexOfPanel(this.getActivePanel());
17168         
17169         if (i  < 1 && !this.autoslide) {
17170             return;
17171         }
17172         
17173         if (i < 1 && this.autoslide) {
17174             i = this.tabs.length;
17175         }
17176         
17177         this.showPanel(this.tabs[i-1]);
17178     },
17179     
17180     
17181     addBullet: function()
17182     {
17183         if(!this.bullets || Roo.isTouch){
17184             return;
17185         }
17186         var ctr = this.el.select('.carousel-bullets',true).first();
17187         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17188         var bullet = ctr.createChild({
17189             cls : 'bullet bullet-' + i
17190         },ctr.dom.lastChild);
17191         
17192         
17193         var _this = this;
17194         
17195         bullet.on('click', (function(e, el, o, ii, t){
17196
17197             e.preventDefault();
17198
17199             this.showPanel(ii);
17200
17201             if(this.autoslide && this.slideFn){
17202                 clearInterval(this.slideFn);
17203                 this.slideFn = window.setInterval(function() {
17204                     _this.showPanelNext();
17205                 }, this.timer);
17206             }
17207
17208         }).createDelegate(this, [i, bullet], true));
17209                 
17210         
17211     },
17212      
17213     setActiveBullet : function(i)
17214     {
17215         if(Roo.isTouch){
17216             return;
17217         }
17218         
17219         Roo.each(this.el.select('.bullet', true).elements, function(el){
17220             el.removeClass('selected');
17221         });
17222
17223         var bullet = this.el.select('.bullet-' + i, true).first();
17224         
17225         if(!bullet){
17226             return;
17227         }
17228         
17229         bullet.addClass('selected');
17230     }
17231     
17232     
17233   
17234 });
17235
17236  
17237
17238  
17239  
17240 Roo.apply(Roo.bootstrap.TabGroup, {
17241     
17242     groups: {},
17243      /**
17244     * register a Navigation Group
17245     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17246     */
17247     register : function(navgrp)
17248     {
17249         this.groups[navgrp.navId] = navgrp;
17250         
17251     },
17252     /**
17253     * fetch a Navigation Group based on the navigation ID
17254     * if one does not exist , it will get created.
17255     * @param {string} the navgroup to add
17256     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17257     */
17258     get: function(navId) {
17259         if (typeof(this.groups[navId]) == 'undefined') {
17260             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17261         }
17262         return this.groups[navId] ;
17263     }
17264     
17265     
17266     
17267 });
17268
17269  /*
17270  * - LGPL
17271  *
17272  * TabPanel
17273  * 
17274  */
17275
17276 /**
17277  * @class Roo.bootstrap.TabPanel
17278  * @extends Roo.bootstrap.Component
17279  * Bootstrap TabPanel class
17280  * @cfg {Boolean} active panel active
17281  * @cfg {String} html panel content
17282  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17283  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17284  * @cfg {String} href click to link..
17285  * 
17286  * 
17287  * @constructor
17288  * Create a new TabPanel
17289  * @param {Object} config The config object
17290  */
17291
17292 Roo.bootstrap.TabPanel = function(config){
17293     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17294     this.addEvents({
17295         /**
17296              * @event changed
17297              * Fires when the active status changes
17298              * @param {Roo.bootstrap.TabPanel} this
17299              * @param {Boolean} state the new state
17300             
17301          */
17302         'changed': true,
17303         /**
17304              * @event beforedeactivate
17305              * Fires before a tab is de-activated - can be used to do validation on a form.
17306              * @param {Roo.bootstrap.TabPanel} this
17307              * @return {Boolean} false if there is an error
17308             
17309          */
17310         'beforedeactivate': true
17311      });
17312     
17313     this.tabId = this.tabId || Roo.id();
17314   
17315 };
17316
17317 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17318     
17319     active: false,
17320     html: false,
17321     tabId: false,
17322     navId : false,
17323     href : '',
17324     
17325     getAutoCreate : function(){
17326         var cfg = {
17327             tag: 'div',
17328             // item is needed for carousel - not sure if it has any effect otherwise
17329             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17330             html: this.html || ''
17331         };
17332         
17333         if(this.active){
17334             cfg.cls += ' active';
17335         }
17336         
17337         if(this.tabId){
17338             cfg.tabId = this.tabId;
17339         }
17340         
17341         
17342         return cfg;
17343     },
17344     
17345     initEvents:  function()
17346     {
17347         var p = this.parent();
17348         
17349         this.navId = this.navId || p.navId;
17350         
17351         if (typeof(this.navId) != 'undefined') {
17352             // not really needed.. but just in case.. parent should be a NavGroup.
17353             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17354             
17355             tg.register(this);
17356             
17357             var i = tg.tabs.length - 1;
17358             
17359             if(this.active && tg.bullets > 0 && i < tg.bullets){
17360                 tg.setActiveBullet(i);
17361             }
17362         }
17363         
17364         this.el.on('click', this.onClick, this);
17365         
17366         if(Roo.isTouch){
17367             this.el.on("touchstart", this.onTouchStart, this);
17368             this.el.on("touchmove", this.onTouchMove, this);
17369             this.el.on("touchend", this.onTouchEnd, this);
17370         }
17371         
17372     },
17373     
17374     onRender : function(ct, position)
17375     {
17376         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17377     },
17378     
17379     setActive : function(state)
17380     {
17381         Roo.log("panel - set active " + this.tabId + "=" + state);
17382         
17383         this.active = state;
17384         if (!state) {
17385             this.el.removeClass('active');
17386             
17387         } else  if (!this.el.hasClass('active')) {
17388             this.el.addClass('active');
17389         }
17390         
17391         this.fireEvent('changed', this, state);
17392     },
17393     
17394     onClick : function(e)
17395     {
17396         e.preventDefault();
17397         
17398         if(!this.href.length){
17399             return;
17400         }
17401         
17402         window.location.href = this.href;
17403     },
17404     
17405     startX : 0,
17406     startY : 0,
17407     endX : 0,
17408     endY : 0,
17409     swiping : false,
17410     
17411     onTouchStart : function(e)
17412     {
17413         this.swiping = false;
17414         
17415         this.startX = e.browserEvent.touches[0].clientX;
17416         this.startY = e.browserEvent.touches[0].clientY;
17417     },
17418     
17419     onTouchMove : function(e)
17420     {
17421         this.swiping = true;
17422         
17423         this.endX = e.browserEvent.touches[0].clientX;
17424         this.endY = e.browserEvent.touches[0].clientY;
17425     },
17426     
17427     onTouchEnd : function(e)
17428     {
17429         if(!this.swiping){
17430             this.onClick(e);
17431             return;
17432         }
17433         
17434         var tabGroup = this.parent();
17435         
17436         if(this.endX > this.startX){ // swiping right
17437             tabGroup.showPanelPrev();
17438             return;
17439         }
17440         
17441         if(this.startX > this.endX){ // swiping left
17442             tabGroup.showPanelNext();
17443             return;
17444         }
17445     }
17446     
17447     
17448 });
17449  
17450
17451  
17452
17453  /*
17454  * - LGPL
17455  *
17456  * DateField
17457  * 
17458  */
17459
17460 /**
17461  * @class Roo.bootstrap.DateField
17462  * @extends Roo.bootstrap.Input
17463  * Bootstrap DateField class
17464  * @cfg {Number} weekStart default 0
17465  * @cfg {String} viewMode default empty, (months|years)
17466  * @cfg {String} minViewMode default empty, (months|years)
17467  * @cfg {Number} startDate default -Infinity
17468  * @cfg {Number} endDate default Infinity
17469  * @cfg {Boolean} todayHighlight default false
17470  * @cfg {Boolean} todayBtn default false
17471  * @cfg {Boolean} calendarWeeks default false
17472  * @cfg {Object} daysOfWeekDisabled default empty
17473  * @cfg {Boolean} singleMode default false (true | false)
17474  * 
17475  * @cfg {Boolean} keyboardNavigation default true
17476  * @cfg {String} language default en
17477  * 
17478  * @constructor
17479  * Create a new DateField
17480  * @param {Object} config The config object
17481  */
17482
17483 Roo.bootstrap.DateField = function(config){
17484     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17485      this.addEvents({
17486             /**
17487              * @event show
17488              * Fires when this field show.
17489              * @param {Roo.bootstrap.DateField} this
17490              * @param {Mixed} date The date value
17491              */
17492             show : true,
17493             /**
17494              * @event show
17495              * Fires when this field hide.
17496              * @param {Roo.bootstrap.DateField} this
17497              * @param {Mixed} date The date value
17498              */
17499             hide : true,
17500             /**
17501              * @event select
17502              * Fires when select a date.
17503              * @param {Roo.bootstrap.DateField} this
17504              * @param {Mixed} date The date value
17505              */
17506             select : true,
17507             /**
17508              * @event beforeselect
17509              * Fires when before select a date.
17510              * @param {Roo.bootstrap.DateField} this
17511              * @param {Mixed} date The date value
17512              */
17513             beforeselect : true
17514         });
17515 };
17516
17517 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
17518     
17519     /**
17520      * @cfg {String} format
17521      * The default date format string which can be overriden for localization support.  The format must be
17522      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17523      */
17524     format : "m/d/y",
17525     /**
17526      * @cfg {String} altFormats
17527      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17528      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17529      */
17530     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17531     
17532     weekStart : 0,
17533     
17534     viewMode : '',
17535     
17536     minViewMode : '',
17537     
17538     todayHighlight : false,
17539     
17540     todayBtn: false,
17541     
17542     language: 'en',
17543     
17544     keyboardNavigation: true,
17545     
17546     calendarWeeks: false,
17547     
17548     startDate: -Infinity,
17549     
17550     endDate: Infinity,
17551     
17552     daysOfWeekDisabled: [],
17553     
17554     _events: [],
17555     
17556     singleMode : false,
17557     
17558     UTCDate: function()
17559     {
17560         return new Date(Date.UTC.apply(Date, arguments));
17561     },
17562     
17563     UTCToday: function()
17564     {
17565         var today = new Date();
17566         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17567     },
17568     
17569     getDate: function() {
17570             var d = this.getUTCDate();
17571             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17572     },
17573     
17574     getUTCDate: function() {
17575             return this.date;
17576     },
17577     
17578     setDate: function(d) {
17579             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17580     },
17581     
17582     setUTCDate: function(d) {
17583             this.date = d;
17584             this.setValue(this.formatDate(this.date));
17585     },
17586         
17587     onRender: function(ct, position)
17588     {
17589         
17590         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17591         
17592         this.language = this.language || 'en';
17593         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17594         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17595         
17596         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17597         this.format = this.format || 'm/d/y';
17598         this.isInline = false;
17599         this.isInput = true;
17600         this.component = this.el.select('.add-on', true).first() || false;
17601         this.component = (this.component && this.component.length === 0) ? false : this.component;
17602         this.hasInput = this.component && this.inputEl().length;
17603         
17604         if (typeof(this.minViewMode === 'string')) {
17605             switch (this.minViewMode) {
17606                 case 'months':
17607                     this.minViewMode = 1;
17608                     break;
17609                 case 'years':
17610                     this.minViewMode = 2;
17611                     break;
17612                 default:
17613                     this.minViewMode = 0;
17614                     break;
17615             }
17616         }
17617         
17618         if (typeof(this.viewMode === 'string')) {
17619             switch (this.viewMode) {
17620                 case 'months':
17621                     this.viewMode = 1;
17622                     break;
17623                 case 'years':
17624                     this.viewMode = 2;
17625                     break;
17626                 default:
17627                     this.viewMode = 0;
17628                     break;
17629             }
17630         }
17631                 
17632         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17633         
17634 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17635         
17636         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17637         
17638         this.picker().on('mousedown', this.onMousedown, this);
17639         this.picker().on('click', this.onClick, this);
17640         
17641         this.picker().addClass('datepicker-dropdown');
17642         
17643         this.startViewMode = this.viewMode;
17644         
17645         if(this.singleMode){
17646             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17647                 v.setVisibilityMode(Roo.Element.DISPLAY);
17648                 v.hide();
17649             });
17650             
17651             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17652                 v.setStyle('width', '189px');
17653             });
17654         }
17655         
17656         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17657             if(!this.calendarWeeks){
17658                 v.remove();
17659                 return;
17660             }
17661             
17662             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17663             v.attr('colspan', function(i, val){
17664                 return parseInt(val) + 1;
17665             });
17666         });
17667                         
17668         
17669         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17670         
17671         this.setStartDate(this.startDate);
17672         this.setEndDate(this.endDate);
17673         
17674         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17675         
17676         this.fillDow();
17677         this.fillMonths();
17678         this.update();
17679         this.showMode();
17680         
17681         if(this.isInline) {
17682             this.show();
17683         }
17684     },
17685     
17686     picker : function()
17687     {
17688         return this.pickerEl;
17689 //        return this.el.select('.datepicker', true).first();
17690     },
17691     
17692     fillDow: function()
17693     {
17694         var dowCnt = this.weekStart;
17695         
17696         var dow = {
17697             tag: 'tr',
17698             cn: [
17699                 
17700             ]
17701         };
17702         
17703         if(this.calendarWeeks){
17704             dow.cn.push({
17705                 tag: 'th',
17706                 cls: 'cw',
17707                 html: '&nbsp;'
17708             })
17709         }
17710         
17711         while (dowCnt < this.weekStart + 7) {
17712             dow.cn.push({
17713                 tag: 'th',
17714                 cls: 'dow',
17715                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17716             });
17717         }
17718         
17719         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17720     },
17721     
17722     fillMonths: function()
17723     {    
17724         var i = 0;
17725         var months = this.picker().select('>.datepicker-months td', true).first();
17726         
17727         months.dom.innerHTML = '';
17728         
17729         while (i < 12) {
17730             var month = {
17731                 tag: 'span',
17732                 cls: 'month',
17733                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17734             };
17735             
17736             months.createChild(month);
17737         }
17738         
17739     },
17740     
17741     update: function()
17742     {
17743         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;
17744         
17745         if (this.date < this.startDate) {
17746             this.viewDate = new Date(this.startDate);
17747         } else if (this.date > this.endDate) {
17748             this.viewDate = new Date(this.endDate);
17749         } else {
17750             this.viewDate = new Date(this.date);
17751         }
17752         
17753         this.fill();
17754     },
17755     
17756     fill: function() 
17757     {
17758         var d = new Date(this.viewDate),
17759                 year = d.getUTCFullYear(),
17760                 month = d.getUTCMonth(),
17761                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17762                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17763                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17764                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17765                 currentDate = this.date && this.date.valueOf(),
17766                 today = this.UTCToday();
17767         
17768         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17769         
17770 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17771         
17772 //        this.picker.select('>tfoot th.today').
17773 //                                              .text(dates[this.language].today)
17774 //                                              .toggle(this.todayBtn !== false);
17775     
17776         this.updateNavArrows();
17777         this.fillMonths();
17778                                                 
17779         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17780         
17781         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17782          
17783         prevMonth.setUTCDate(day);
17784         
17785         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17786         
17787         var nextMonth = new Date(prevMonth);
17788         
17789         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17790         
17791         nextMonth = nextMonth.valueOf();
17792         
17793         var fillMonths = false;
17794         
17795         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17796         
17797         while(prevMonth.valueOf() < nextMonth) {
17798             var clsName = '';
17799             
17800             if (prevMonth.getUTCDay() === this.weekStart) {
17801                 if(fillMonths){
17802                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17803                 }
17804                     
17805                 fillMonths = {
17806                     tag: 'tr',
17807                     cn: []
17808                 };
17809                 
17810                 if(this.calendarWeeks){
17811                     // ISO 8601: First week contains first thursday.
17812                     // ISO also states week starts on Monday, but we can be more abstract here.
17813                     var
17814                     // Start of current week: based on weekstart/current date
17815                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17816                     // Thursday of this week
17817                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17818                     // First Thursday of year, year from thursday
17819                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17820                     // Calendar week: ms between thursdays, div ms per day, div 7 days
17821                     calWeek =  (th - yth) / 864e5 / 7 + 1;
17822                     
17823                     fillMonths.cn.push({
17824                         tag: 'td',
17825                         cls: 'cw',
17826                         html: calWeek
17827                     });
17828                 }
17829             }
17830             
17831             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17832                 clsName += ' old';
17833             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17834                 clsName += ' new';
17835             }
17836             if (this.todayHighlight &&
17837                 prevMonth.getUTCFullYear() == today.getFullYear() &&
17838                 prevMonth.getUTCMonth() == today.getMonth() &&
17839                 prevMonth.getUTCDate() == today.getDate()) {
17840                 clsName += ' today';
17841             }
17842             
17843             if (currentDate && prevMonth.valueOf() === currentDate) {
17844                 clsName += ' active';
17845             }
17846             
17847             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17848                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17849                     clsName += ' disabled';
17850             }
17851             
17852             fillMonths.cn.push({
17853                 tag: 'td',
17854                 cls: 'day ' + clsName,
17855                 html: prevMonth.getDate()
17856             });
17857             
17858             prevMonth.setDate(prevMonth.getDate()+1);
17859         }
17860           
17861         var currentYear = this.date && this.date.getUTCFullYear();
17862         var currentMonth = this.date && this.date.getUTCMonth();
17863         
17864         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17865         
17866         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17867             v.removeClass('active');
17868             
17869             if(currentYear === year && k === currentMonth){
17870                 v.addClass('active');
17871             }
17872             
17873             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17874                 v.addClass('disabled');
17875             }
17876             
17877         });
17878         
17879         
17880         year = parseInt(year/10, 10) * 10;
17881         
17882         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17883         
17884         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17885         
17886         year -= 1;
17887         for (var i = -1; i < 11; i++) {
17888             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17889                 tag: 'span',
17890                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17891                 html: year
17892             });
17893             
17894             year += 1;
17895         }
17896     },
17897     
17898     showMode: function(dir) 
17899     {
17900         if (dir) {
17901             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17902         }
17903         
17904         Roo.each(this.picker().select('>div',true).elements, function(v){
17905             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17906             v.hide();
17907         });
17908         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17909     },
17910     
17911     place: function()
17912     {
17913         if(this.isInline) {
17914             return;
17915         }
17916         
17917         this.picker().removeClass(['bottom', 'top']);
17918         
17919         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17920             /*
17921              * place to the top of element!
17922              *
17923              */
17924             
17925             this.picker().addClass('top');
17926             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17927             
17928             return;
17929         }
17930         
17931         this.picker().addClass('bottom');
17932         
17933         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17934     },
17935     
17936     parseDate : function(value)
17937     {
17938         if(!value || value instanceof Date){
17939             return value;
17940         }
17941         var v = Date.parseDate(value, this.format);
17942         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17943             v = Date.parseDate(value, 'Y-m-d');
17944         }
17945         if(!v && this.altFormats){
17946             if(!this.altFormatsArray){
17947                 this.altFormatsArray = this.altFormats.split("|");
17948             }
17949             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17950                 v = Date.parseDate(value, this.altFormatsArray[i]);
17951             }
17952         }
17953         return v;
17954     },
17955     
17956     formatDate : function(date, fmt)
17957     {   
17958         return (!date || !(date instanceof Date)) ?
17959         date : date.dateFormat(fmt || this.format);
17960     },
17961     
17962     onFocus : function()
17963     {
17964         Roo.bootstrap.DateField.superclass.onFocus.call(this);
17965         this.show();
17966     },
17967     
17968     onBlur : function()
17969     {
17970         Roo.bootstrap.DateField.superclass.onBlur.call(this);
17971         
17972         var d = this.inputEl().getValue();
17973         
17974         this.setValue(d);
17975                 
17976         this.hide();
17977     },
17978     
17979     show : function()
17980     {
17981         this.picker().show();
17982         this.update();
17983         this.place();
17984         
17985         this.fireEvent('show', this, this.date);
17986     },
17987     
17988     hide : function()
17989     {
17990         if(this.isInline) {
17991             return;
17992         }
17993         this.picker().hide();
17994         this.viewMode = this.startViewMode;
17995         this.showMode();
17996         
17997         this.fireEvent('hide', this, this.date);
17998         
17999     },
18000     
18001     onMousedown: function(e)
18002     {
18003         e.stopPropagation();
18004         e.preventDefault();
18005     },
18006     
18007     keyup: function(e)
18008     {
18009         Roo.bootstrap.DateField.superclass.keyup.call(this);
18010         this.update();
18011     },
18012
18013     setValue: function(v)
18014     {
18015         if(this.fireEvent('beforeselect', this, v) !== false){
18016             var d = new Date(this.parseDate(v) ).clearTime();
18017         
18018             if(isNaN(d.getTime())){
18019                 this.date = this.viewDate = '';
18020                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18021                 return;
18022             }
18023
18024             v = this.formatDate(d);
18025
18026             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18027
18028             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18029
18030             this.update();
18031
18032             this.fireEvent('select', this, this.date);
18033         }
18034     },
18035     
18036     getValue: function()
18037     {
18038         return this.formatDate(this.date);
18039     },
18040     
18041     fireKey: function(e)
18042     {
18043         if (!this.picker().isVisible()){
18044             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18045                 this.show();
18046             }
18047             return;
18048         }
18049         
18050         var dateChanged = false,
18051         dir, day, month,
18052         newDate, newViewDate;
18053         
18054         switch(e.keyCode){
18055             case 27: // escape
18056                 this.hide();
18057                 e.preventDefault();
18058                 break;
18059             case 37: // left
18060             case 39: // right
18061                 if (!this.keyboardNavigation) {
18062                     break;
18063                 }
18064                 dir = e.keyCode == 37 ? -1 : 1;
18065                 
18066                 if (e.ctrlKey){
18067                     newDate = this.moveYear(this.date, dir);
18068                     newViewDate = this.moveYear(this.viewDate, dir);
18069                 } else if (e.shiftKey){
18070                     newDate = this.moveMonth(this.date, dir);
18071                     newViewDate = this.moveMonth(this.viewDate, dir);
18072                 } else {
18073                     newDate = new Date(this.date);
18074                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18075                     newViewDate = new Date(this.viewDate);
18076                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18077                 }
18078                 if (this.dateWithinRange(newDate)){
18079                     this.date = newDate;
18080                     this.viewDate = newViewDate;
18081                     this.setValue(this.formatDate(this.date));
18082 //                    this.update();
18083                     e.preventDefault();
18084                     dateChanged = true;
18085                 }
18086                 break;
18087             case 38: // up
18088             case 40: // down
18089                 if (!this.keyboardNavigation) {
18090                     break;
18091                 }
18092                 dir = e.keyCode == 38 ? -1 : 1;
18093                 if (e.ctrlKey){
18094                     newDate = this.moveYear(this.date, dir);
18095                     newViewDate = this.moveYear(this.viewDate, dir);
18096                 } else if (e.shiftKey){
18097                     newDate = this.moveMonth(this.date, dir);
18098                     newViewDate = this.moveMonth(this.viewDate, dir);
18099                 } else {
18100                     newDate = new Date(this.date);
18101                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18102                     newViewDate = new Date(this.viewDate);
18103                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18104                 }
18105                 if (this.dateWithinRange(newDate)){
18106                     this.date = newDate;
18107                     this.viewDate = newViewDate;
18108                     this.setValue(this.formatDate(this.date));
18109 //                    this.update();
18110                     e.preventDefault();
18111                     dateChanged = true;
18112                 }
18113                 break;
18114             case 13: // enter
18115                 this.setValue(this.formatDate(this.date));
18116                 this.hide();
18117                 e.preventDefault();
18118                 break;
18119             case 9: // tab
18120                 this.setValue(this.formatDate(this.date));
18121                 this.hide();
18122                 break;
18123             case 16: // shift
18124             case 17: // ctrl
18125             case 18: // alt
18126                 break;
18127             default :
18128                 this.hide();
18129                 
18130         }
18131     },
18132     
18133     
18134     onClick: function(e) 
18135     {
18136         e.stopPropagation();
18137         e.preventDefault();
18138         
18139         var target = e.getTarget();
18140         
18141         if(target.nodeName.toLowerCase() === 'i'){
18142             target = Roo.get(target).dom.parentNode;
18143         }
18144         
18145         var nodeName = target.nodeName;
18146         var className = target.className;
18147         var html = target.innerHTML;
18148         //Roo.log(nodeName);
18149         
18150         switch(nodeName.toLowerCase()) {
18151             case 'th':
18152                 switch(className) {
18153                     case 'switch':
18154                         this.showMode(1);
18155                         break;
18156                     case 'prev':
18157                     case 'next':
18158                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18159                         switch(this.viewMode){
18160                                 case 0:
18161                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18162                                         break;
18163                                 case 1:
18164                                 case 2:
18165                                         this.viewDate = this.moveYear(this.viewDate, dir);
18166                                         break;
18167                         }
18168                         this.fill();
18169                         break;
18170                     case 'today':
18171                         var date = new Date();
18172                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18173 //                        this.fill()
18174                         this.setValue(this.formatDate(this.date));
18175                         
18176                         this.hide();
18177                         break;
18178                 }
18179                 break;
18180             case 'span':
18181                 if (className.indexOf('disabled') < 0) {
18182                     this.viewDate.setUTCDate(1);
18183                     if (className.indexOf('month') > -1) {
18184                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18185                     } else {
18186                         var year = parseInt(html, 10) || 0;
18187                         this.viewDate.setUTCFullYear(year);
18188                         
18189                     }
18190                     
18191                     if(this.singleMode){
18192                         this.setValue(this.formatDate(this.viewDate));
18193                         this.hide();
18194                         return;
18195                     }
18196                     
18197                     this.showMode(-1);
18198                     this.fill();
18199                 }
18200                 break;
18201                 
18202             case 'td':
18203                 //Roo.log(className);
18204                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18205                     var day = parseInt(html, 10) || 1;
18206                     var year = this.viewDate.getUTCFullYear(),
18207                         month = this.viewDate.getUTCMonth();
18208
18209                     if (className.indexOf('old') > -1) {
18210                         if(month === 0 ){
18211                             month = 11;
18212                             year -= 1;
18213                         }else{
18214                             month -= 1;
18215                         }
18216                     } else if (className.indexOf('new') > -1) {
18217                         if (month == 11) {
18218                             month = 0;
18219                             year += 1;
18220                         } else {
18221                             month += 1;
18222                         }
18223                     }
18224                     //Roo.log([year,month,day]);
18225                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18226                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18227 //                    this.fill();
18228                     //Roo.log(this.formatDate(this.date));
18229                     this.setValue(this.formatDate(this.date));
18230                     this.hide();
18231                 }
18232                 break;
18233         }
18234     },
18235     
18236     setStartDate: function(startDate)
18237     {
18238         this.startDate = startDate || -Infinity;
18239         if (this.startDate !== -Infinity) {
18240             this.startDate = this.parseDate(this.startDate);
18241         }
18242         this.update();
18243         this.updateNavArrows();
18244     },
18245
18246     setEndDate: function(endDate)
18247     {
18248         this.endDate = endDate || Infinity;
18249         if (this.endDate !== Infinity) {
18250             this.endDate = this.parseDate(this.endDate);
18251         }
18252         this.update();
18253         this.updateNavArrows();
18254     },
18255     
18256     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18257     {
18258         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18259         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18260             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18261         }
18262         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18263             return parseInt(d, 10);
18264         });
18265         this.update();
18266         this.updateNavArrows();
18267     },
18268     
18269     updateNavArrows: function() 
18270     {
18271         if(this.singleMode){
18272             return;
18273         }
18274         
18275         var d = new Date(this.viewDate),
18276         year = d.getUTCFullYear(),
18277         month = d.getUTCMonth();
18278         
18279         Roo.each(this.picker().select('.prev', true).elements, function(v){
18280             v.show();
18281             switch (this.viewMode) {
18282                 case 0:
18283
18284                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18285                         v.hide();
18286                     }
18287                     break;
18288                 case 1:
18289                 case 2:
18290                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18291                         v.hide();
18292                     }
18293                     break;
18294             }
18295         });
18296         
18297         Roo.each(this.picker().select('.next', true).elements, function(v){
18298             v.show();
18299             switch (this.viewMode) {
18300                 case 0:
18301
18302                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18303                         v.hide();
18304                     }
18305                     break;
18306                 case 1:
18307                 case 2:
18308                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18309                         v.hide();
18310                     }
18311                     break;
18312             }
18313         })
18314     },
18315     
18316     moveMonth: function(date, dir)
18317     {
18318         if (!dir) {
18319             return date;
18320         }
18321         var new_date = new Date(date.valueOf()),
18322         day = new_date.getUTCDate(),
18323         month = new_date.getUTCMonth(),
18324         mag = Math.abs(dir),
18325         new_month, test;
18326         dir = dir > 0 ? 1 : -1;
18327         if (mag == 1){
18328             test = dir == -1
18329             // If going back one month, make sure month is not current month
18330             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18331             ? function(){
18332                 return new_date.getUTCMonth() == month;
18333             }
18334             // If going forward one month, make sure month is as expected
18335             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18336             : function(){
18337                 return new_date.getUTCMonth() != new_month;
18338             };
18339             new_month = month + dir;
18340             new_date.setUTCMonth(new_month);
18341             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18342             if (new_month < 0 || new_month > 11) {
18343                 new_month = (new_month + 12) % 12;
18344             }
18345         } else {
18346             // For magnitudes >1, move one month at a time...
18347             for (var i=0; i<mag; i++) {
18348                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18349                 new_date = this.moveMonth(new_date, dir);
18350             }
18351             // ...then reset the day, keeping it in the new month
18352             new_month = new_date.getUTCMonth();
18353             new_date.setUTCDate(day);
18354             test = function(){
18355                 return new_month != new_date.getUTCMonth();
18356             };
18357         }
18358         // Common date-resetting loop -- if date is beyond end of month, make it
18359         // end of month
18360         while (test()){
18361             new_date.setUTCDate(--day);
18362             new_date.setUTCMonth(new_month);
18363         }
18364         return new_date;
18365     },
18366
18367     moveYear: function(date, dir)
18368     {
18369         return this.moveMonth(date, dir*12);
18370     },
18371
18372     dateWithinRange: function(date)
18373     {
18374         return date >= this.startDate && date <= this.endDate;
18375     },
18376
18377     
18378     remove: function() 
18379     {
18380         this.picker().remove();
18381     },
18382     
18383     validateValue : function(value)
18384     {
18385         if(value.length < 1)  {
18386             if(this.allowBlank){
18387                 return true;
18388             }
18389             return false;
18390         }
18391         
18392         if(value.length < this.minLength){
18393             return false;
18394         }
18395         if(value.length > this.maxLength){
18396             return false;
18397         }
18398         if(this.vtype){
18399             var vt = Roo.form.VTypes;
18400             if(!vt[this.vtype](value, this)){
18401                 return false;
18402             }
18403         }
18404         if(typeof this.validator == "function"){
18405             var msg = this.validator(value);
18406             if(msg !== true){
18407                 return false;
18408             }
18409         }
18410         
18411         if(this.regex && !this.regex.test(value)){
18412             return false;
18413         }
18414         
18415         if(typeof(this.parseDate(value)) == 'undefined'){
18416             return false;
18417         }
18418         
18419         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18420             return false;
18421         }      
18422         
18423         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18424             return false;
18425         } 
18426         
18427         
18428         return true;
18429     }
18430    
18431 });
18432
18433 Roo.apply(Roo.bootstrap.DateField,  {
18434     
18435     head : {
18436         tag: 'thead',
18437         cn: [
18438         {
18439             tag: 'tr',
18440             cn: [
18441             {
18442                 tag: 'th',
18443                 cls: 'prev',
18444                 html: '<i class="fa fa-arrow-left"/>'
18445             },
18446             {
18447                 tag: 'th',
18448                 cls: 'switch',
18449                 colspan: '5'
18450             },
18451             {
18452                 tag: 'th',
18453                 cls: 'next',
18454                 html: '<i class="fa fa-arrow-right"/>'
18455             }
18456
18457             ]
18458         }
18459         ]
18460     },
18461     
18462     content : {
18463         tag: 'tbody',
18464         cn: [
18465         {
18466             tag: 'tr',
18467             cn: [
18468             {
18469                 tag: 'td',
18470                 colspan: '7'
18471             }
18472             ]
18473         }
18474         ]
18475     },
18476     
18477     footer : {
18478         tag: 'tfoot',
18479         cn: [
18480         {
18481             tag: 'tr',
18482             cn: [
18483             {
18484                 tag: 'th',
18485                 colspan: '7',
18486                 cls: 'today'
18487             }
18488                     
18489             ]
18490         }
18491         ]
18492     },
18493     
18494     dates:{
18495         en: {
18496             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18497             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18498             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18499             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18500             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18501             today: "Today"
18502         }
18503     },
18504     
18505     modes: [
18506     {
18507         clsName: 'days',
18508         navFnc: 'Month',
18509         navStep: 1
18510     },
18511     {
18512         clsName: 'months',
18513         navFnc: 'FullYear',
18514         navStep: 1
18515     },
18516     {
18517         clsName: 'years',
18518         navFnc: 'FullYear',
18519         navStep: 10
18520     }]
18521 });
18522
18523 Roo.apply(Roo.bootstrap.DateField,  {
18524   
18525     template : {
18526         tag: 'div',
18527         cls: 'datepicker dropdown-menu roo-dynamic',
18528         cn: [
18529         {
18530             tag: 'div',
18531             cls: 'datepicker-days',
18532             cn: [
18533             {
18534                 tag: 'table',
18535                 cls: 'table-condensed',
18536                 cn:[
18537                 Roo.bootstrap.DateField.head,
18538                 {
18539                     tag: 'tbody'
18540                 },
18541                 Roo.bootstrap.DateField.footer
18542                 ]
18543             }
18544             ]
18545         },
18546         {
18547             tag: 'div',
18548             cls: 'datepicker-months',
18549             cn: [
18550             {
18551                 tag: 'table',
18552                 cls: 'table-condensed',
18553                 cn:[
18554                 Roo.bootstrap.DateField.head,
18555                 Roo.bootstrap.DateField.content,
18556                 Roo.bootstrap.DateField.footer
18557                 ]
18558             }
18559             ]
18560         },
18561         {
18562             tag: 'div',
18563             cls: 'datepicker-years',
18564             cn: [
18565             {
18566                 tag: 'table',
18567                 cls: 'table-condensed',
18568                 cn:[
18569                 Roo.bootstrap.DateField.head,
18570                 Roo.bootstrap.DateField.content,
18571                 Roo.bootstrap.DateField.footer
18572                 ]
18573             }
18574             ]
18575         }
18576         ]
18577     }
18578 });
18579
18580  
18581
18582  /*
18583  * - LGPL
18584  *
18585  * TimeField
18586  * 
18587  */
18588
18589 /**
18590  * @class Roo.bootstrap.TimeField
18591  * @extends Roo.bootstrap.Input
18592  * Bootstrap DateField class
18593  * 
18594  * 
18595  * @constructor
18596  * Create a new TimeField
18597  * @param {Object} config The config object
18598  */
18599
18600 Roo.bootstrap.TimeField = function(config){
18601     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18602     this.addEvents({
18603             /**
18604              * @event show
18605              * Fires when this field show.
18606              * @param {Roo.bootstrap.DateField} thisthis
18607              * @param {Mixed} date The date value
18608              */
18609             show : true,
18610             /**
18611              * @event show
18612              * Fires when this field hide.
18613              * @param {Roo.bootstrap.DateField} this
18614              * @param {Mixed} date The date value
18615              */
18616             hide : true,
18617             /**
18618              * @event select
18619              * Fires when select a date.
18620              * @param {Roo.bootstrap.DateField} this
18621              * @param {Mixed} date The date value
18622              */
18623             select : true
18624         });
18625 };
18626
18627 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
18628     
18629     /**
18630      * @cfg {String} format
18631      * The default time format string which can be overriden for localization support.  The format must be
18632      * valid according to {@link Date#parseDate} (defaults to 'H:i').
18633      */
18634     format : "H:i",
18635        
18636     onRender: function(ct, position)
18637     {
18638         
18639         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18640                 
18641         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18642         
18643         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18644         
18645         this.pop = this.picker().select('>.datepicker-time',true).first();
18646         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18647         
18648         this.picker().on('mousedown', this.onMousedown, this);
18649         this.picker().on('click', this.onClick, this);
18650         
18651         this.picker().addClass('datepicker-dropdown');
18652     
18653         this.fillTime();
18654         this.update();
18655             
18656         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18657         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18658         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18659         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18660         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18661         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18662
18663     },
18664     
18665     fireKey: function(e){
18666         if (!this.picker().isVisible()){
18667             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18668                 this.show();
18669             }
18670             return;
18671         }
18672
18673         e.preventDefault();
18674         
18675         switch(e.keyCode){
18676             case 27: // escape
18677                 this.hide();
18678                 break;
18679             case 37: // left
18680             case 39: // right
18681                 this.onTogglePeriod();
18682                 break;
18683             case 38: // up
18684                 this.onIncrementMinutes();
18685                 break;
18686             case 40: // down
18687                 this.onDecrementMinutes();
18688                 break;
18689             case 13: // enter
18690             case 9: // tab
18691                 this.setTime();
18692                 break;
18693         }
18694     },
18695     
18696     onClick: function(e) {
18697         e.stopPropagation();
18698         e.preventDefault();
18699     },
18700     
18701     picker : function()
18702     {
18703         return this.el.select('.datepicker', true).first();
18704     },
18705     
18706     fillTime: function()
18707     {    
18708         var time = this.pop.select('tbody', true).first();
18709         
18710         time.dom.innerHTML = '';
18711         
18712         time.createChild({
18713             tag: 'tr',
18714             cn: [
18715                 {
18716                     tag: 'td',
18717                     cn: [
18718                         {
18719                             tag: 'a',
18720                             href: '#',
18721                             cls: 'btn',
18722                             cn: [
18723                                 {
18724                                     tag: 'span',
18725                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
18726                                 }
18727                             ]
18728                         } 
18729                     ]
18730                 },
18731                 {
18732                     tag: 'td',
18733                     cls: 'separator'
18734                 },
18735                 {
18736                     tag: 'td',
18737                     cn: [
18738                         {
18739                             tag: 'a',
18740                             href: '#',
18741                             cls: 'btn',
18742                             cn: [
18743                                 {
18744                                     tag: 'span',
18745                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
18746                                 }
18747                             ]
18748                         }
18749                     ]
18750                 },
18751                 {
18752                     tag: 'td',
18753                     cls: 'separator'
18754                 }
18755             ]
18756         });
18757         
18758         time.createChild({
18759             tag: 'tr',
18760             cn: [
18761                 {
18762                     tag: 'td',
18763                     cn: [
18764                         {
18765                             tag: 'span',
18766                             cls: 'timepicker-hour',
18767                             html: '00'
18768                         }  
18769                     ]
18770                 },
18771                 {
18772                     tag: 'td',
18773                     cls: 'separator',
18774                     html: ':'
18775                 },
18776                 {
18777                     tag: 'td',
18778                     cn: [
18779                         {
18780                             tag: 'span',
18781                             cls: 'timepicker-minute',
18782                             html: '00'
18783                         }  
18784                     ]
18785                 },
18786                 {
18787                     tag: 'td',
18788                     cls: 'separator'
18789                 },
18790                 {
18791                     tag: 'td',
18792                     cn: [
18793                         {
18794                             tag: 'button',
18795                             type: 'button',
18796                             cls: 'btn btn-primary period',
18797                             html: 'AM'
18798                             
18799                         }
18800                     ]
18801                 }
18802             ]
18803         });
18804         
18805         time.createChild({
18806             tag: 'tr',
18807             cn: [
18808                 {
18809                     tag: 'td',
18810                     cn: [
18811                         {
18812                             tag: 'a',
18813                             href: '#',
18814                             cls: 'btn',
18815                             cn: [
18816                                 {
18817                                     tag: 'span',
18818                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
18819                                 }
18820                             ]
18821                         }
18822                     ]
18823                 },
18824                 {
18825                     tag: 'td',
18826                     cls: 'separator'
18827                 },
18828                 {
18829                     tag: 'td',
18830                     cn: [
18831                         {
18832                             tag: 'a',
18833                             href: '#',
18834                             cls: 'btn',
18835                             cn: [
18836                                 {
18837                                     tag: 'span',
18838                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
18839                                 }
18840                             ]
18841                         }
18842                     ]
18843                 },
18844                 {
18845                     tag: 'td',
18846                     cls: 'separator'
18847                 }
18848             ]
18849         });
18850         
18851     },
18852     
18853     update: function()
18854     {
18855         
18856         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18857         
18858         this.fill();
18859     },
18860     
18861     fill: function() 
18862     {
18863         var hours = this.time.getHours();
18864         var minutes = this.time.getMinutes();
18865         var period = 'AM';
18866         
18867         if(hours > 11){
18868             period = 'PM';
18869         }
18870         
18871         if(hours == 0){
18872             hours = 12;
18873         }
18874         
18875         
18876         if(hours > 12){
18877             hours = hours - 12;
18878         }
18879         
18880         if(hours < 10){
18881             hours = '0' + hours;
18882         }
18883         
18884         if(minutes < 10){
18885             minutes = '0' + minutes;
18886         }
18887         
18888         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18889         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18890         this.pop.select('button', true).first().dom.innerHTML = period;
18891         
18892     },
18893     
18894     place: function()
18895     {   
18896         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18897         
18898         var cls = ['bottom'];
18899         
18900         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18901             cls.pop();
18902             cls.push('top');
18903         }
18904         
18905         cls.push('right');
18906         
18907         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18908             cls.pop();
18909             cls.push('left');
18910         }
18911         
18912         this.picker().addClass(cls.join('-'));
18913         
18914         var _this = this;
18915         
18916         Roo.each(cls, function(c){
18917             if(c == 'bottom'){
18918                 _this.picker().setTop(_this.inputEl().getHeight());
18919                 return;
18920             }
18921             if(c == 'top'){
18922                 _this.picker().setTop(0 - _this.picker().getHeight());
18923                 return;
18924             }
18925             
18926             if(c == 'left'){
18927                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18928                 return;
18929             }
18930             if(c == 'right'){
18931                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18932                 return;
18933             }
18934         });
18935         
18936     },
18937   
18938     onFocus : function()
18939     {
18940         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18941         this.show();
18942     },
18943     
18944     onBlur : function()
18945     {
18946         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18947         this.hide();
18948     },
18949     
18950     show : function()
18951     {
18952         this.picker().show();
18953         this.pop.show();
18954         this.update();
18955         this.place();
18956         
18957         this.fireEvent('show', this, this.date);
18958     },
18959     
18960     hide : function()
18961     {
18962         this.picker().hide();
18963         this.pop.hide();
18964         
18965         this.fireEvent('hide', this, this.date);
18966     },
18967     
18968     setTime : function()
18969     {
18970         this.hide();
18971         this.setValue(this.time.format(this.format));
18972         
18973         this.fireEvent('select', this, this.date);
18974         
18975         
18976     },
18977     
18978     onMousedown: function(e){
18979         e.stopPropagation();
18980         e.preventDefault();
18981     },
18982     
18983     onIncrementHours: function()
18984     {
18985         Roo.log('onIncrementHours');
18986         this.time = this.time.add(Date.HOUR, 1);
18987         this.update();
18988         
18989     },
18990     
18991     onDecrementHours: function()
18992     {
18993         Roo.log('onDecrementHours');
18994         this.time = this.time.add(Date.HOUR, -1);
18995         this.update();
18996     },
18997     
18998     onIncrementMinutes: function()
18999     {
19000         Roo.log('onIncrementMinutes');
19001         this.time = this.time.add(Date.MINUTE, 1);
19002         this.update();
19003     },
19004     
19005     onDecrementMinutes: function()
19006     {
19007         Roo.log('onDecrementMinutes');
19008         this.time = this.time.add(Date.MINUTE, -1);
19009         this.update();
19010     },
19011     
19012     onTogglePeriod: function()
19013     {
19014         Roo.log('onTogglePeriod');
19015         this.time = this.time.add(Date.HOUR, 12);
19016         this.update();
19017     }
19018     
19019    
19020 });
19021
19022 Roo.apply(Roo.bootstrap.TimeField,  {
19023     
19024     content : {
19025         tag: 'tbody',
19026         cn: [
19027             {
19028                 tag: 'tr',
19029                 cn: [
19030                 {
19031                     tag: 'td',
19032                     colspan: '7'
19033                 }
19034                 ]
19035             }
19036         ]
19037     },
19038     
19039     footer : {
19040         tag: 'tfoot',
19041         cn: [
19042             {
19043                 tag: 'tr',
19044                 cn: [
19045                 {
19046                     tag: 'th',
19047                     colspan: '7',
19048                     cls: '',
19049                     cn: [
19050                         {
19051                             tag: 'button',
19052                             cls: 'btn btn-info ok',
19053                             html: 'OK'
19054                         }
19055                     ]
19056                 }
19057
19058                 ]
19059             }
19060         ]
19061     }
19062 });
19063
19064 Roo.apply(Roo.bootstrap.TimeField,  {
19065   
19066     template : {
19067         tag: 'div',
19068         cls: 'datepicker dropdown-menu',
19069         cn: [
19070             {
19071                 tag: 'div',
19072                 cls: 'datepicker-time',
19073                 cn: [
19074                 {
19075                     tag: 'table',
19076                     cls: 'table-condensed',
19077                     cn:[
19078                     Roo.bootstrap.TimeField.content,
19079                     Roo.bootstrap.TimeField.footer
19080                     ]
19081                 }
19082                 ]
19083             }
19084         ]
19085     }
19086 });
19087
19088  
19089
19090  /*
19091  * - LGPL
19092  *
19093  * MonthField
19094  * 
19095  */
19096
19097 /**
19098  * @class Roo.bootstrap.MonthField
19099  * @extends Roo.bootstrap.Input
19100  * Bootstrap MonthField class
19101  * 
19102  * @cfg {String} language default en
19103  * 
19104  * @constructor
19105  * Create a new MonthField
19106  * @param {Object} config The config object
19107  */
19108
19109 Roo.bootstrap.MonthField = function(config){
19110     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19111     
19112     this.addEvents({
19113         /**
19114          * @event show
19115          * Fires when this field show.
19116          * @param {Roo.bootstrap.MonthField} this
19117          * @param {Mixed} date The date value
19118          */
19119         show : true,
19120         /**
19121          * @event show
19122          * Fires when this field hide.
19123          * @param {Roo.bootstrap.MonthField} this
19124          * @param {Mixed} date The date value
19125          */
19126         hide : true,
19127         /**
19128          * @event select
19129          * Fires when select a date.
19130          * @param {Roo.bootstrap.MonthField} this
19131          * @param {String} oldvalue The old value
19132          * @param {String} newvalue The new value
19133          */
19134         select : true
19135     });
19136 };
19137
19138 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19139     
19140     onRender: function(ct, position)
19141     {
19142         
19143         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19144         
19145         this.language = this.language || 'en';
19146         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19147         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19148         
19149         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19150         this.isInline = false;
19151         this.isInput = true;
19152         this.component = this.el.select('.add-on', true).first() || false;
19153         this.component = (this.component && this.component.length === 0) ? false : this.component;
19154         this.hasInput = this.component && this.inputEL().length;
19155         
19156         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19157         
19158         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19159         
19160         this.picker().on('mousedown', this.onMousedown, this);
19161         this.picker().on('click', this.onClick, this);
19162         
19163         this.picker().addClass('datepicker-dropdown');
19164         
19165         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19166             v.setStyle('width', '189px');
19167         });
19168         
19169         this.fillMonths();
19170         
19171         this.update();
19172         
19173         if(this.isInline) {
19174             this.show();
19175         }
19176         
19177     },
19178     
19179     setValue: function(v, suppressEvent)
19180     {   
19181         var o = this.getValue();
19182         
19183         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19184         
19185         this.update();
19186
19187         if(suppressEvent !== true){
19188             this.fireEvent('select', this, o, v);
19189         }
19190         
19191     },
19192     
19193     getValue: function()
19194     {
19195         return this.value;
19196     },
19197     
19198     onClick: function(e) 
19199     {
19200         e.stopPropagation();
19201         e.preventDefault();
19202         
19203         var target = e.getTarget();
19204         
19205         if(target.nodeName.toLowerCase() === 'i'){
19206             target = Roo.get(target).dom.parentNode;
19207         }
19208         
19209         var nodeName = target.nodeName;
19210         var className = target.className;
19211         var html = target.innerHTML;
19212         
19213         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19214             return;
19215         }
19216         
19217         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19218         
19219         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19220         
19221         this.hide();
19222                         
19223     },
19224     
19225     picker : function()
19226     {
19227         return this.pickerEl;
19228     },
19229     
19230     fillMonths: function()
19231     {    
19232         var i = 0;
19233         var months = this.picker().select('>.datepicker-months td', true).first();
19234         
19235         months.dom.innerHTML = '';
19236         
19237         while (i < 12) {
19238             var month = {
19239                 tag: 'span',
19240                 cls: 'month',
19241                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19242             };
19243             
19244             months.createChild(month);
19245         }
19246         
19247     },
19248     
19249     update: function()
19250     {
19251         var _this = this;
19252         
19253         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19254             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19255         }
19256         
19257         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19258             e.removeClass('active');
19259             
19260             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19261                 e.addClass('active');
19262             }
19263         })
19264     },
19265     
19266     place: function()
19267     {
19268         if(this.isInline) {
19269             return;
19270         }
19271         
19272         this.picker().removeClass(['bottom', 'top']);
19273         
19274         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19275             /*
19276              * place to the top of element!
19277              *
19278              */
19279             
19280             this.picker().addClass('top');
19281             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19282             
19283             return;
19284         }
19285         
19286         this.picker().addClass('bottom');
19287         
19288         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19289     },
19290     
19291     onFocus : function()
19292     {
19293         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19294         this.show();
19295     },
19296     
19297     onBlur : function()
19298     {
19299         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19300         
19301         var d = this.inputEl().getValue();
19302         
19303         this.setValue(d);
19304                 
19305         this.hide();
19306     },
19307     
19308     show : function()
19309     {
19310         this.picker().show();
19311         this.picker().select('>.datepicker-months', true).first().show();
19312         this.update();
19313         this.place();
19314         
19315         this.fireEvent('show', this, this.date);
19316     },
19317     
19318     hide : function()
19319     {
19320         if(this.isInline) {
19321             return;
19322         }
19323         this.picker().hide();
19324         this.fireEvent('hide', this, this.date);
19325         
19326     },
19327     
19328     onMousedown: function(e)
19329     {
19330         e.stopPropagation();
19331         e.preventDefault();
19332     },
19333     
19334     keyup: function(e)
19335     {
19336         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19337         this.update();
19338     },
19339
19340     fireKey: function(e)
19341     {
19342         if (!this.picker().isVisible()){
19343             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19344                 this.show();
19345             }
19346             return;
19347         }
19348         
19349         var dir;
19350         
19351         switch(e.keyCode){
19352             case 27: // escape
19353                 this.hide();
19354                 e.preventDefault();
19355                 break;
19356             case 37: // left
19357             case 39: // right
19358                 dir = e.keyCode == 37 ? -1 : 1;
19359                 
19360                 this.vIndex = this.vIndex + dir;
19361                 
19362                 if(this.vIndex < 0){
19363                     this.vIndex = 0;
19364                 }
19365                 
19366                 if(this.vIndex > 11){
19367                     this.vIndex = 11;
19368                 }
19369                 
19370                 if(isNaN(this.vIndex)){
19371                     this.vIndex = 0;
19372                 }
19373                 
19374                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19375                 
19376                 break;
19377             case 38: // up
19378             case 40: // down
19379                 
19380                 dir = e.keyCode == 38 ? -1 : 1;
19381                 
19382                 this.vIndex = this.vIndex + dir * 4;
19383                 
19384                 if(this.vIndex < 0){
19385                     this.vIndex = 0;
19386                 }
19387                 
19388                 if(this.vIndex > 11){
19389                     this.vIndex = 11;
19390                 }
19391                 
19392                 if(isNaN(this.vIndex)){
19393                     this.vIndex = 0;
19394                 }
19395                 
19396                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19397                 break;
19398                 
19399             case 13: // enter
19400                 
19401                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19402                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19403                 }
19404                 
19405                 this.hide();
19406                 e.preventDefault();
19407                 break;
19408             case 9: // tab
19409                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19410                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19411                 }
19412                 this.hide();
19413                 break;
19414             case 16: // shift
19415             case 17: // ctrl
19416             case 18: // alt
19417                 break;
19418             default :
19419                 this.hide();
19420                 
19421         }
19422     },
19423     
19424     remove: function() 
19425     {
19426         this.picker().remove();
19427     }
19428    
19429 });
19430
19431 Roo.apply(Roo.bootstrap.MonthField,  {
19432     
19433     content : {
19434         tag: 'tbody',
19435         cn: [
19436         {
19437             tag: 'tr',
19438             cn: [
19439             {
19440                 tag: 'td',
19441                 colspan: '7'
19442             }
19443             ]
19444         }
19445         ]
19446     },
19447     
19448     dates:{
19449         en: {
19450             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19451             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19452         }
19453     }
19454 });
19455
19456 Roo.apply(Roo.bootstrap.MonthField,  {
19457   
19458     template : {
19459         tag: 'div',
19460         cls: 'datepicker dropdown-menu roo-dynamic',
19461         cn: [
19462             {
19463                 tag: 'div',
19464                 cls: 'datepicker-months',
19465                 cn: [
19466                 {
19467                     tag: 'table',
19468                     cls: 'table-condensed',
19469                     cn:[
19470                         Roo.bootstrap.DateField.content
19471                     ]
19472                 }
19473                 ]
19474             }
19475         ]
19476     }
19477 });
19478
19479  
19480
19481  
19482  /*
19483  * - LGPL
19484  *
19485  * CheckBox
19486  * 
19487  */
19488
19489 /**
19490  * @class Roo.bootstrap.CheckBox
19491  * @extends Roo.bootstrap.Input
19492  * Bootstrap CheckBox class
19493  * 
19494  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19495  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19496  * @cfg {String} boxLabel The text that appears beside the checkbox
19497  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19498  * @cfg {Boolean} checked initnal the element
19499  * @cfg {Boolean} inline inline the element (default false)
19500  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19501  * 
19502  * @constructor
19503  * Create a new CheckBox
19504  * @param {Object} config The config object
19505  */
19506
19507 Roo.bootstrap.CheckBox = function(config){
19508     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19509    
19510     this.addEvents({
19511         /**
19512         * @event check
19513         * Fires when the element is checked or unchecked.
19514         * @param {Roo.bootstrap.CheckBox} this This input
19515         * @param {Boolean} checked The new checked value
19516         */
19517        check : true
19518     });
19519     
19520 };
19521
19522 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
19523   
19524     inputType: 'checkbox',
19525     inputValue: 1,
19526     valueOff: 0,
19527     boxLabel: false,
19528     checked: false,
19529     weight : false,
19530     inline: false,
19531     
19532     getAutoCreate : function()
19533     {
19534         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19535         
19536         var id = Roo.id();
19537         
19538         var cfg = {};
19539         
19540         cfg.cls = 'form-group ' + this.inputType; //input-group
19541         
19542         if(this.inline){
19543             cfg.cls += ' ' + this.inputType + '-inline';
19544         }
19545         
19546         var input =  {
19547             tag: 'input',
19548             id : id,
19549             type : this.inputType,
19550             value : this.inputValue,
19551             cls : 'roo-' + this.inputType, //'form-box',
19552             placeholder : this.placeholder || ''
19553             
19554         };
19555         
19556         if(this.inputType != 'radio'){
19557             var hidden =  {
19558                 tag: 'input',
19559                 type : 'hidden',
19560                 cls : 'roo-hidden-value',
19561                 value : this.checked ? this.valueOff : this.inputValue
19562             };
19563         }
19564         
19565             
19566         if (this.weight) { // Validity check?
19567             cfg.cls += " " + this.inputType + "-" + this.weight;
19568         }
19569         
19570         if (this.disabled) {
19571             input.disabled=true;
19572         }
19573         
19574         if(this.checked){
19575             input.checked = this.checked;
19576             
19577         }
19578         
19579         
19580         if (this.name) {
19581             
19582             input.name = this.name;
19583             
19584             if(this.inputType != 'radio'){
19585                 hidden.name = this.name;
19586                 input.name = '_hidden_' + this.name;
19587             }
19588         }
19589         
19590         if (this.size) {
19591             input.cls += ' input-' + this.size;
19592         }
19593         
19594         var settings=this;
19595         
19596         ['xs','sm','md','lg'].map(function(size){
19597             if (settings[size]) {
19598                 cfg.cls += ' col-' + size + '-' + settings[size];
19599             }
19600         });
19601         
19602         var inputblock = input;
19603          
19604         if (this.before || this.after) {
19605             
19606             inputblock = {
19607                 cls : 'input-group',
19608                 cn :  [] 
19609             };
19610             
19611             if (this.before) {
19612                 inputblock.cn.push({
19613                     tag :'span',
19614                     cls : 'input-group-addon',
19615                     html : this.before
19616                 });
19617             }
19618             
19619             inputblock.cn.push(input);
19620             
19621             if(this.inputType != 'radio'){
19622                 inputblock.cn.push(hidden);
19623             }
19624             
19625             if (this.after) {
19626                 inputblock.cn.push({
19627                     tag :'span',
19628                     cls : 'input-group-addon',
19629                     html : this.after
19630                 });
19631             }
19632             
19633         }
19634         
19635         if (align ==='left' && this.fieldLabel.length) {
19636 //                Roo.log("left and has label");
19637                 cfg.cn = [
19638                     
19639                     {
19640                         tag: 'label',
19641                         'for' :  id,
19642                         cls : 'control-label col-md-' + this.labelWidth,
19643                         html : this.fieldLabel
19644                         
19645                     },
19646                     {
19647                         cls : "col-md-" + (12 - this.labelWidth), 
19648                         cn: [
19649                             inputblock
19650                         ]
19651                     }
19652                     
19653                 ];
19654         } else if ( this.fieldLabel.length) {
19655 //                Roo.log(" label");
19656                 cfg.cn = [
19657                    
19658                     {
19659                         tag: this.boxLabel ? 'span' : 'label',
19660                         'for': id,
19661                         cls: 'control-label box-input-label',
19662                         //cls : 'input-group-addon',
19663                         html : this.fieldLabel
19664                         
19665                     },
19666                     
19667                     inputblock
19668                     
19669                 ];
19670
19671         } else {
19672             
19673 //                Roo.log(" no label && no align");
19674                 cfg.cn = [  inputblock ] ;
19675                 
19676                 
19677         }
19678         
19679         if(this.boxLabel){
19680              var boxLabelCfg = {
19681                 tag: 'label',
19682                 //'for': id, // box label is handled by onclick - so no for...
19683                 cls: 'box-label',
19684                 html: this.boxLabel
19685             };
19686             
19687             if(this.tooltip){
19688                 boxLabelCfg.tooltip = this.tooltip;
19689             }
19690              
19691             cfg.cn.push(boxLabelCfg);
19692         }
19693         
19694         if(this.inputType != 'radio'){
19695             cfg.cn.push(hidden);
19696         }
19697         
19698         return cfg;
19699         
19700     },
19701     
19702     /**
19703      * return the real input element.
19704      */
19705     inputEl: function ()
19706     {
19707         return this.el.select('input.roo-' + this.inputType,true).first();
19708     },
19709     hiddenEl: function ()
19710     {
19711         return this.el.select('input.roo-hidden-value',true).first();
19712     },
19713     
19714     labelEl: function()
19715     {
19716         return this.el.select('label.control-label',true).first();
19717     },
19718     /* depricated... */
19719     
19720     label: function()
19721     {
19722         return this.labelEl();
19723     },
19724     
19725     boxLabelEl: function()
19726     {
19727         return this.el.select('label.box-label',true).first();
19728     },
19729     
19730     initEvents : function()
19731     {
19732 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19733         
19734         this.inputEl().on('click', this.onClick,  this);
19735         
19736         if (this.boxLabel) { 
19737             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
19738         }
19739         
19740         this.startValue = this.getValue();
19741         
19742         if(this.groupId){
19743             Roo.bootstrap.CheckBox.register(this);
19744         }
19745     },
19746     
19747     onClick : function()
19748     {   
19749         this.setChecked(!this.checked);
19750     },
19751     
19752     setChecked : function(state,suppressEvent)
19753     {
19754         this.startValue = this.getValue();
19755         
19756         if(this.inputType == 'radio'){
19757             
19758             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19759                 e.dom.checked = false;
19760             });
19761             
19762             this.inputEl().dom.checked = true;
19763             
19764             this.inputEl().dom.value = this.inputValue;
19765             
19766             if(suppressEvent !== true){
19767                 this.fireEvent('check', this, true);
19768             }
19769             
19770             this.validate();
19771             
19772             return;
19773         }
19774         
19775         this.checked = state;
19776         
19777         this.inputEl().dom.checked = state;
19778         
19779         
19780         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
19781         
19782         if(suppressEvent !== true){
19783             this.fireEvent('check', this, state);
19784         }
19785         
19786         this.validate();
19787     },
19788     
19789     getValue : function()
19790     {
19791         if(this.inputType == 'radio'){
19792             return this.getGroupValue();
19793         }
19794         
19795         return this.hiddenEl().dom.value;
19796         
19797     },
19798     
19799     getGroupValue : function()
19800     {
19801         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19802             return '';
19803         }
19804         
19805         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19806     },
19807     
19808     setValue : function(v,suppressEvent)
19809     {
19810         if(this.inputType == 'radio'){
19811             this.setGroupValue(v, suppressEvent);
19812             return;
19813         }
19814         
19815         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19816         
19817         this.validate();
19818     },
19819     
19820     setGroupValue : function(v, suppressEvent)
19821     {
19822         this.startValue = this.getValue();
19823         
19824         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19825             e.dom.checked = false;
19826             
19827             if(e.dom.value == v){
19828                 e.dom.checked = true;
19829             }
19830         });
19831         
19832         if(suppressEvent !== true){
19833             this.fireEvent('check', this, true);
19834         }
19835
19836         this.validate();
19837         
19838         return;
19839     },
19840     
19841     validate : function()
19842     {
19843         if(
19844                 this.disabled || 
19845                 (this.inputType == 'radio' && this.validateRadio()) ||
19846                 (this.inputType == 'checkbox' && this.validateCheckbox())
19847         ){
19848             this.markValid();
19849             return true;
19850         }
19851         
19852         this.markInvalid();
19853         return false;
19854     },
19855     
19856     validateRadio : function()
19857     {
19858         if(this.allowBlank){
19859             return true;
19860         }
19861         
19862         var valid = false;
19863         
19864         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19865             if(!e.dom.checked){
19866                 return;
19867             }
19868             
19869             valid = true;
19870             
19871             return false;
19872         });
19873         
19874         return valid;
19875     },
19876     
19877     validateCheckbox : function()
19878     {
19879         if(!this.groupId){
19880             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19881         }
19882         
19883         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19884         
19885         if(!group){
19886             return false;
19887         }
19888         
19889         var r = false;
19890         
19891         for(var i in group){
19892             if(r){
19893                 break;
19894             }
19895             
19896             r = (group[i].getValue() == group[i].inputValue) ? true : false;
19897         }
19898         
19899         return r;
19900     },
19901     
19902     /**
19903      * Mark this field as valid
19904      */
19905     markValid : function()
19906     {
19907         if(this.allowBlank){
19908             return;
19909         }
19910         
19911         var _this = this;
19912         
19913         this.fireEvent('valid', this);
19914         
19915         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19916         
19917         if(this.groupId){
19918             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19919         }
19920         
19921         if(label){
19922             label.markValid();
19923         }
19924
19925         if(this.inputType == 'radio'){
19926             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19927                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19928                 e.findParent('.form-group', false, true).addClass(_this.validClass);
19929             });
19930             
19931             return;
19932         }
19933         
19934         if(!this.groupId){
19935             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19936             this.el.findParent('.form-group', false, true).addClass(this.validClass);
19937             return;
19938         }
19939         
19940         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19941             
19942         if(!group){
19943             return;
19944         }
19945         
19946         for(var i in group){
19947             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19948             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19949         }
19950     },
19951     
19952      /**
19953      * Mark this field as invalid
19954      * @param {String} msg The validation message
19955      */
19956     markInvalid : function(msg)
19957     {
19958         if(this.allowBlank){
19959             return;
19960         }
19961         
19962         var _this = this;
19963         
19964         this.fireEvent('invalid', this, msg);
19965         
19966         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19967         
19968         if(this.groupId){
19969             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19970         }
19971         
19972         if(label){
19973             label.markInvalid();
19974         }
19975             
19976         if(this.inputType == 'radio'){
19977             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19978                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19979                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19980             });
19981             
19982             return;
19983         }
19984         
19985         if(!this.groupId){
19986             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19987             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19988             return;
19989         }
19990         
19991         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19992         
19993         if(!group){
19994             return;
19995         }
19996         
19997         for(var i in group){
19998             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19999             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20000         }
20001         
20002     },
20003     
20004     disable : function()
20005     {
20006         if(this.inputType != 'radio'){
20007             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20008             return;
20009         }
20010         
20011         var _this = this;
20012         
20013         if(this.rendered){
20014             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20015                 _this.getActionEl().addClass(this.disabledClass);
20016                 e.dom.disabled = true;
20017             });
20018         }
20019         
20020         this.disabled = true;
20021         this.fireEvent("disable", this);
20022         return this;
20023     },
20024
20025     enable : function()
20026     {
20027         if(this.inputType != 'radio'){
20028             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20029             return;
20030         }
20031         
20032         var _this = this;
20033         
20034         if(this.rendered){
20035             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20036                 _this.getActionEl().removeClass(this.disabledClass);
20037                 e.dom.disabled = false;
20038             });
20039         }
20040         
20041         this.disabled = false;
20042         this.fireEvent("enable", this);
20043         return this;
20044     }
20045
20046 });
20047
20048 Roo.apply(Roo.bootstrap.CheckBox, {
20049     
20050     groups: {},
20051     
20052      /**
20053     * register a CheckBox Group
20054     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20055     */
20056     register : function(checkbox)
20057     {
20058         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20059             this.groups[checkbox.groupId] = {};
20060         }
20061         
20062         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20063             return;
20064         }
20065         
20066         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20067         
20068     },
20069     /**
20070     * fetch a CheckBox Group based on the group ID
20071     * @param {string} the group ID
20072     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20073     */
20074     get: function(groupId) {
20075         if (typeof(this.groups[groupId]) == 'undefined') {
20076             return false;
20077         }
20078         
20079         return this.groups[groupId] ;
20080     }
20081     
20082     
20083 });
20084 /*
20085  * - LGPL
20086  *
20087  * Radio
20088  *
20089  *
20090  * not inline
20091  *<div class="radio">
20092   <label>
20093     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
20094     Option one is this and that&mdash;be sure to include why it's great
20095   </label>
20096 </div>
20097  *
20098  *
20099  *inline
20100  *<span>
20101  *<label class="radio-inline">fieldLabel</label>
20102  *<label class="radio-inline">
20103   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
20104 </label>
20105 <span>
20106  *
20107  *
20108  */
20109
20110 /**
20111  * @class Roo.bootstrap.Radio
20112  * @extends Roo.bootstrap.CheckBox
20113  * Bootstrap Radio class
20114
20115  * @constructor
20116  * Create a new Radio
20117  * @param {Object} config The config object
20118  */
20119
20120 Roo.bootstrap.Radio = function(config){
20121     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20122
20123 };
20124
20125 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
20126
20127     inputType: 'radio',
20128     inputValue: '',
20129     valueOff: '',
20130
20131     getAutoCreate : function()
20132     {
20133         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20134         align = align || 'left'; // default...
20135
20136
20137
20138         var id = Roo.id();
20139
20140         var cfg = {
20141                 tag : this.inline ? 'span' : 'div',
20142                 cls : 'form-group',
20143                 cn : []
20144         };
20145
20146         var inline = this.inline ? ' radio-inline' : '';
20147
20148         var lbl = {
20149                 tag: 'label' ,
20150                 // does not need for, as we wrap the input with it..
20151                 'for' : id,
20152                 cls : 'control-label box-label' + inline,
20153                 cn : []
20154         };
20155         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
20156
20157         var fieldLabel = {
20158             tag: 'label' ,
20159             //cls : 'control-label' + inline,
20160             html : this.fieldLabel,
20161             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
20162         };
20163
20164         var input =  {
20165             tag: 'input',
20166             id : id,
20167             type : this.inputType,
20168             //value : (!this.checked) ? this.valueOff : this.inputValue,
20169             value : this.inputValue,
20170             cls : 'roo-radio',
20171             placeholder : this.placeholder || '' // ?? needed????
20172
20173         };
20174         if (this.weight) { // Validity check?
20175             input.cls += " radio-" + this.weight;
20176         }
20177         if (this.disabled) {
20178             input.disabled=true;
20179         }
20180
20181         if(this.checked){
20182             input.checked = this.checked;
20183         }
20184
20185         if (this.name) {
20186             input.name = this.name;
20187         }
20188
20189         if (this.size) {
20190             input.cls += ' input-' + this.size;
20191         }
20192
20193         //?? can span's inline have a width??
20194
20195         var settings=this;
20196         ['xs','sm','md','lg'].map(function(size){
20197             if (settings[size]) {
20198                 cfg.cls += ' col-' + size + '-' + settings[size];
20199             }
20200         });
20201
20202         var inputblock = input;
20203
20204         if (this.before || this.after) {
20205
20206             inputblock = {
20207                 cls : 'input-group',
20208                 tag : 'span',
20209                 cn :  []
20210             };
20211             if (this.before) {
20212                 inputblock.cn.push({
20213                     tag :'span',
20214                     cls : 'input-group-addon',
20215                     html : this.before
20216                 });
20217             }
20218             inputblock.cn.push(input);
20219             if (this.after) {
20220                 inputblock.cn.push({
20221                     tag :'span',
20222                     cls : 'input-group-addon',
20223                     html : this.after
20224                 });
20225             }
20226
20227         };
20228
20229
20230         if (this.fieldLabel && this.fieldLabel.length) {
20231             cfg.cn.push(fieldLabel);
20232         }
20233
20234         // normal bootstrap puts the input inside the label.
20235         // however with our styled version - it has to go after the input.
20236
20237         //lbl.cn.push(inputblock);
20238
20239         var lblwrap =  {
20240             tag: 'span',
20241             cls: 'radio' + inline,
20242             cn: [
20243                 inputblock,
20244                 lbl
20245             ]
20246         };
20247
20248         cfg.cn.push( lblwrap);
20249
20250         if(this.boxLabel){
20251             lbl.cn.push({
20252                 tag: 'span',
20253                 html: this.boxLabel
20254             })
20255         }
20256
20257
20258         return cfg;
20259
20260     },
20261
20262     initEvents : function()
20263     {
20264 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20265
20266         this.inputEl().on('click', this.onClick,  this);
20267         if (this.boxLabel) {
20268             //Roo.log('find label');
20269             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
20270         }
20271
20272     },
20273
20274     inputEl: function ()
20275     {
20276         return this.el.select('input.roo-radio',true).first();
20277     },
20278     onClick : function()
20279     {
20280         Roo.log("click");
20281         this.setChecked(true);
20282     },
20283
20284     setChecked : function(state,suppressEvent)
20285     {
20286         if(state){
20287             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20288                 v.dom.checked = false;
20289             });
20290         }
20291         Roo.log(this.inputEl().dom);
20292         this.checked = state;
20293         this.inputEl().dom.checked = state;
20294
20295         if(suppressEvent !== true){
20296             this.fireEvent('check', this, state);
20297         }
20298
20299         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
20300
20301     },
20302
20303     getGroupValue : function()
20304     {
20305         var value = '';
20306         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20307             if(v.dom.checked == true){
20308                 value = v.dom.value;
20309             }
20310         });
20311
20312         return value;
20313     },
20314
20315     /**
20316      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
20317      * @return {Mixed} value The field value
20318      */
20319     getValue : function(){
20320         return this.getGroupValue();
20321     }
20322
20323 });
20324 //<script type="text/javascript">
20325
20326 /*
20327  * Based  Ext JS Library 1.1.1
20328  * Copyright(c) 2006-2007, Ext JS, LLC.
20329  * LGPL
20330  *
20331  */
20332  
20333 /**
20334  * @class Roo.HtmlEditorCore
20335  * @extends Roo.Component
20336  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20337  *
20338  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20339  */
20340
20341 Roo.HtmlEditorCore = function(config){
20342     
20343     
20344     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20345     
20346     
20347     this.addEvents({
20348         /**
20349          * @event initialize
20350          * Fires when the editor is fully initialized (including the iframe)
20351          * @param {Roo.HtmlEditorCore} this
20352          */
20353         initialize: true,
20354         /**
20355          * @event activate
20356          * Fires when the editor is first receives the focus. Any insertion must wait
20357          * until after this event.
20358          * @param {Roo.HtmlEditorCore} this
20359          */
20360         activate: true,
20361          /**
20362          * @event beforesync
20363          * Fires before the textarea is updated with content from the editor iframe. Return false
20364          * to cancel the sync.
20365          * @param {Roo.HtmlEditorCore} this
20366          * @param {String} html
20367          */
20368         beforesync: true,
20369          /**
20370          * @event beforepush
20371          * Fires before the iframe editor is updated with content from the textarea. Return false
20372          * to cancel the push.
20373          * @param {Roo.HtmlEditorCore} this
20374          * @param {String} html
20375          */
20376         beforepush: true,
20377          /**
20378          * @event sync
20379          * Fires when the textarea is updated with content from the editor iframe.
20380          * @param {Roo.HtmlEditorCore} this
20381          * @param {String} html
20382          */
20383         sync: true,
20384          /**
20385          * @event push
20386          * Fires when the iframe editor is updated with content from the textarea.
20387          * @param {Roo.HtmlEditorCore} this
20388          * @param {String} html
20389          */
20390         push: true,
20391         
20392         /**
20393          * @event editorevent
20394          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20395          * @param {Roo.HtmlEditorCore} this
20396          */
20397         editorevent: true
20398         
20399     });
20400     
20401     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20402     
20403     // defaults : white / black...
20404     this.applyBlacklists();
20405     
20406     
20407     
20408 };
20409
20410
20411 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20412
20413
20414      /**
20415      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20416      */
20417     
20418     owner : false,
20419     
20420      /**
20421      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20422      *                        Roo.resizable.
20423      */
20424     resizable : false,
20425      /**
20426      * @cfg {Number} height (in pixels)
20427      */   
20428     height: 300,
20429    /**
20430      * @cfg {Number} width (in pixels)
20431      */   
20432     width: 500,
20433     
20434     /**
20435      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20436      * 
20437      */
20438     stylesheets: false,
20439     
20440     // id of frame..
20441     frameId: false,
20442     
20443     // private properties
20444     validationEvent : false,
20445     deferHeight: true,
20446     initialized : false,
20447     activated : false,
20448     sourceEditMode : false,
20449     onFocus : Roo.emptyFn,
20450     iframePad:3,
20451     hideMode:'offsets',
20452     
20453     clearUp: true,
20454     
20455     // blacklist + whitelisted elements..
20456     black: false,
20457     white: false,
20458      
20459     
20460
20461     /**
20462      * Protected method that will not generally be called directly. It
20463      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20464      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20465      */
20466     getDocMarkup : function(){
20467         // body styles..
20468         var st = '';
20469         
20470         // inherit styels from page...?? 
20471         if (this.stylesheets === false) {
20472             
20473             Roo.get(document.head).select('style').each(function(node) {
20474                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20475             });
20476             
20477             Roo.get(document.head).select('link').each(function(node) { 
20478                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20479             });
20480             
20481         } else if (!this.stylesheets.length) {
20482                 // simple..
20483                 st = '<style type="text/css">' +
20484                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20485                    '</style>';
20486         } else { 
20487             
20488         }
20489         
20490         st +=  '<style type="text/css">' +
20491             'IMG { cursor: pointer } ' +
20492         '</style>';
20493
20494         
20495         return '<html><head>' + st  +
20496             //<style type="text/css">' +
20497             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20498             //'</style>' +
20499             ' </head><body class="roo-htmleditor-body"></body></html>';
20500     },
20501
20502     // private
20503     onRender : function(ct, position)
20504     {
20505         var _t = this;
20506         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20507         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20508         
20509         
20510         this.el.dom.style.border = '0 none';
20511         this.el.dom.setAttribute('tabIndex', -1);
20512         this.el.addClass('x-hidden hide');
20513         
20514         
20515         
20516         if(Roo.isIE){ // fix IE 1px bogus margin
20517             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20518         }
20519        
20520         
20521         this.frameId = Roo.id();
20522         
20523          
20524         
20525         var iframe = this.owner.wrap.createChild({
20526             tag: 'iframe',
20527             cls: 'form-control', // bootstrap..
20528             id: this.frameId,
20529             name: this.frameId,
20530             frameBorder : 'no',
20531             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20532         }, this.el
20533         );
20534         
20535         
20536         this.iframe = iframe.dom;
20537
20538          this.assignDocWin();
20539         
20540         this.doc.designMode = 'on';
20541        
20542         this.doc.open();
20543         this.doc.write(this.getDocMarkup());
20544         this.doc.close();
20545
20546         
20547         var task = { // must defer to wait for browser to be ready
20548             run : function(){
20549                 //console.log("run task?" + this.doc.readyState);
20550                 this.assignDocWin();
20551                 if(this.doc.body || this.doc.readyState == 'complete'){
20552                     try {
20553                         this.doc.designMode="on";
20554                     } catch (e) {
20555                         return;
20556                     }
20557                     Roo.TaskMgr.stop(task);
20558                     this.initEditor.defer(10, this);
20559                 }
20560             },
20561             interval : 10,
20562             duration: 10000,
20563             scope: this
20564         };
20565         Roo.TaskMgr.start(task);
20566
20567     },
20568
20569     // private
20570     onResize : function(w, h)
20571     {
20572          Roo.log('resize: ' +w + ',' + h );
20573         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20574         if(!this.iframe){
20575             return;
20576         }
20577         if(typeof w == 'number'){
20578             
20579             this.iframe.style.width = w + 'px';
20580         }
20581         if(typeof h == 'number'){
20582             
20583             this.iframe.style.height = h + 'px';
20584             if(this.doc){
20585                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20586             }
20587         }
20588         
20589     },
20590
20591     /**
20592      * Toggles the editor between standard and source edit mode.
20593      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20594      */
20595     toggleSourceEdit : function(sourceEditMode){
20596         
20597         this.sourceEditMode = sourceEditMode === true;
20598         
20599         if(this.sourceEditMode){
20600  
20601             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20602             
20603         }else{
20604             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20605             //this.iframe.className = '';
20606             this.deferFocus();
20607         }
20608         //this.setSize(this.owner.wrap.getSize());
20609         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20610     },
20611
20612     
20613   
20614
20615     /**
20616      * Protected method that will not generally be called directly. If you need/want
20617      * custom HTML cleanup, this is the method you should override.
20618      * @param {String} html The HTML to be cleaned
20619      * return {String} The cleaned HTML
20620      */
20621     cleanHtml : function(html){
20622         html = String(html);
20623         if(html.length > 5){
20624             if(Roo.isSafari){ // strip safari nonsense
20625                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20626             }
20627         }
20628         if(html == '&nbsp;'){
20629             html = '';
20630         }
20631         return html;
20632     },
20633
20634     /**
20635      * HTML Editor -> Textarea
20636      * Protected method that will not generally be called directly. Syncs the contents
20637      * of the editor iframe with the textarea.
20638      */
20639     syncValue : function(){
20640         if(this.initialized){
20641             var bd = (this.doc.body || this.doc.documentElement);
20642             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20643             var html = bd.innerHTML;
20644             if(Roo.isSafari){
20645                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20646                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20647                 if(m && m[1]){
20648                     html = '<div style="'+m[0]+'">' + html + '</div>';
20649                 }
20650             }
20651             html = this.cleanHtml(html);
20652             // fix up the special chars.. normaly like back quotes in word...
20653             // however we do not want to do this with chinese..
20654             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20655                 var cc = b.charCodeAt();
20656                 if (
20657                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20658                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20659                     (cc >= 0xf900 && cc < 0xfb00 )
20660                 ) {
20661                         return b;
20662                 }
20663                 return "&#"+cc+";" 
20664             });
20665             if(this.owner.fireEvent('beforesync', this, html) !== false){
20666                 this.el.dom.value = html;
20667                 this.owner.fireEvent('sync', this, html);
20668             }
20669         }
20670     },
20671
20672     /**
20673      * Protected method that will not generally be called directly. Pushes the value of the textarea
20674      * into the iframe editor.
20675      */
20676     pushValue : function(){
20677         if(this.initialized){
20678             var v = this.el.dom.value.trim();
20679             
20680 //            if(v.length < 1){
20681 //                v = '&#160;';
20682 //            }
20683             
20684             if(this.owner.fireEvent('beforepush', this, v) !== false){
20685                 var d = (this.doc.body || this.doc.documentElement);
20686                 d.innerHTML = v;
20687                 this.cleanUpPaste();
20688                 this.el.dom.value = d.innerHTML;
20689                 this.owner.fireEvent('push', this, v);
20690             }
20691         }
20692     },
20693
20694     // private
20695     deferFocus : function(){
20696         this.focus.defer(10, this);
20697     },
20698
20699     // doc'ed in Field
20700     focus : function(){
20701         if(this.win && !this.sourceEditMode){
20702             this.win.focus();
20703         }else{
20704             this.el.focus();
20705         }
20706     },
20707     
20708     assignDocWin: function()
20709     {
20710         var iframe = this.iframe;
20711         
20712          if(Roo.isIE){
20713             this.doc = iframe.contentWindow.document;
20714             this.win = iframe.contentWindow;
20715         } else {
20716 //            if (!Roo.get(this.frameId)) {
20717 //                return;
20718 //            }
20719 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20720 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20721             
20722             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20723                 return;
20724             }
20725             
20726             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20727             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20728         }
20729     },
20730     
20731     // private
20732     initEditor : function(){
20733         //console.log("INIT EDITOR");
20734         this.assignDocWin();
20735         
20736         
20737         
20738         this.doc.designMode="on";
20739         this.doc.open();
20740         this.doc.write(this.getDocMarkup());
20741         this.doc.close();
20742         
20743         var dbody = (this.doc.body || this.doc.documentElement);
20744         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20745         // this copies styles from the containing element into thsi one..
20746         // not sure why we need all of this..
20747         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20748         
20749         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20750         //ss['background-attachment'] = 'fixed'; // w3c
20751         dbody.bgProperties = 'fixed'; // ie
20752         //Roo.DomHelper.applyStyles(dbody, ss);
20753         Roo.EventManager.on(this.doc, {
20754             //'mousedown': this.onEditorEvent,
20755             'mouseup': this.onEditorEvent,
20756             'dblclick': this.onEditorEvent,
20757             'click': this.onEditorEvent,
20758             'keyup': this.onEditorEvent,
20759             buffer:100,
20760             scope: this
20761         });
20762         if(Roo.isGecko){
20763             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20764         }
20765         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20766             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20767         }
20768         this.initialized = true;
20769
20770         this.owner.fireEvent('initialize', this);
20771         this.pushValue();
20772     },
20773
20774     // private
20775     onDestroy : function(){
20776         
20777         
20778         
20779         if(this.rendered){
20780             
20781             //for (var i =0; i < this.toolbars.length;i++) {
20782             //    // fixme - ask toolbars for heights?
20783             //    this.toolbars[i].onDestroy();
20784            // }
20785             
20786             //this.wrap.dom.innerHTML = '';
20787             //this.wrap.remove();
20788         }
20789     },
20790
20791     // private
20792     onFirstFocus : function(){
20793         
20794         this.assignDocWin();
20795         
20796         
20797         this.activated = true;
20798          
20799     
20800         if(Roo.isGecko){ // prevent silly gecko errors
20801             this.win.focus();
20802             var s = this.win.getSelection();
20803             if(!s.focusNode || s.focusNode.nodeType != 3){
20804                 var r = s.getRangeAt(0);
20805                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20806                 r.collapse(true);
20807                 this.deferFocus();
20808             }
20809             try{
20810                 this.execCmd('useCSS', true);
20811                 this.execCmd('styleWithCSS', false);
20812             }catch(e){}
20813         }
20814         this.owner.fireEvent('activate', this);
20815     },
20816
20817     // private
20818     adjustFont: function(btn){
20819         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20820         //if(Roo.isSafari){ // safari
20821         //    adjust *= 2;
20822        // }
20823         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20824         if(Roo.isSafari){ // safari
20825             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20826             v =  (v < 10) ? 10 : v;
20827             v =  (v > 48) ? 48 : v;
20828             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20829             
20830         }
20831         
20832         
20833         v = Math.max(1, v+adjust);
20834         
20835         this.execCmd('FontSize', v  );
20836     },
20837
20838     onEditorEvent : function(e)
20839     {
20840         this.owner.fireEvent('editorevent', this, e);
20841       //  this.updateToolbar();
20842         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20843     },
20844
20845     insertTag : function(tg)
20846     {
20847         // could be a bit smarter... -> wrap the current selected tRoo..
20848         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20849             
20850             range = this.createRange(this.getSelection());
20851             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20852             wrappingNode.appendChild(range.extractContents());
20853             range.insertNode(wrappingNode);
20854
20855             return;
20856             
20857             
20858             
20859         }
20860         this.execCmd("formatblock",   tg);
20861         
20862     },
20863     
20864     insertText : function(txt)
20865     {
20866         
20867         
20868         var range = this.createRange();
20869         range.deleteContents();
20870                //alert(Sender.getAttribute('label'));
20871                
20872         range.insertNode(this.doc.createTextNode(txt));
20873     } ,
20874     
20875      
20876
20877     /**
20878      * Executes a Midas editor command on the editor document and performs necessary focus and
20879      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20880      * @param {String} cmd The Midas command
20881      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20882      */
20883     relayCmd : function(cmd, value){
20884         this.win.focus();
20885         this.execCmd(cmd, value);
20886         this.owner.fireEvent('editorevent', this);
20887         //this.updateToolbar();
20888         this.owner.deferFocus();
20889     },
20890
20891     /**
20892      * Executes a Midas editor command directly on the editor document.
20893      * For visual commands, you should use {@link #relayCmd} instead.
20894      * <b>This should only be called after the editor is initialized.</b>
20895      * @param {String} cmd The Midas command
20896      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20897      */
20898     execCmd : function(cmd, value){
20899         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20900         this.syncValue();
20901     },
20902  
20903  
20904    
20905     /**
20906      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20907      * to insert tRoo.
20908      * @param {String} text | dom node.. 
20909      */
20910     insertAtCursor : function(text)
20911     {
20912         
20913         
20914         
20915         if(!this.activated){
20916             return;
20917         }
20918         /*
20919         if(Roo.isIE){
20920             this.win.focus();
20921             var r = this.doc.selection.createRange();
20922             if(r){
20923                 r.collapse(true);
20924                 r.pasteHTML(text);
20925                 this.syncValue();
20926                 this.deferFocus();
20927             
20928             }
20929             return;
20930         }
20931         */
20932         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20933             this.win.focus();
20934             
20935             
20936             // from jquery ui (MIT licenced)
20937             var range, node;
20938             var win = this.win;
20939             
20940             if (win.getSelection && win.getSelection().getRangeAt) {
20941                 range = win.getSelection().getRangeAt(0);
20942                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20943                 range.insertNode(node);
20944             } else if (win.document.selection && win.document.selection.createRange) {
20945                 // no firefox support
20946                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20947                 win.document.selection.createRange().pasteHTML(txt);
20948             } else {
20949                 // no firefox support
20950                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20951                 this.execCmd('InsertHTML', txt);
20952             } 
20953             
20954             this.syncValue();
20955             
20956             this.deferFocus();
20957         }
20958     },
20959  // private
20960     mozKeyPress : function(e){
20961         if(e.ctrlKey){
20962             var c = e.getCharCode(), cmd;
20963           
20964             if(c > 0){
20965                 c = String.fromCharCode(c).toLowerCase();
20966                 switch(c){
20967                     case 'b':
20968                         cmd = 'bold';
20969                         break;
20970                     case 'i':
20971                         cmd = 'italic';
20972                         break;
20973                     
20974                     case 'u':
20975                         cmd = 'underline';
20976                         break;
20977                     
20978                     case 'v':
20979                         this.cleanUpPaste.defer(100, this);
20980                         return;
20981                         
20982                 }
20983                 if(cmd){
20984                     this.win.focus();
20985                     this.execCmd(cmd);
20986                     this.deferFocus();
20987                     e.preventDefault();
20988                 }
20989                 
20990             }
20991         }
20992     },
20993
20994     // private
20995     fixKeys : function(){ // load time branching for fastest keydown performance
20996         if(Roo.isIE){
20997             return function(e){
20998                 var k = e.getKey(), r;
20999                 if(k == e.TAB){
21000                     e.stopEvent();
21001                     r = this.doc.selection.createRange();
21002                     if(r){
21003                         r.collapse(true);
21004                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21005                         this.deferFocus();
21006                     }
21007                     return;
21008                 }
21009                 
21010                 if(k == e.ENTER){
21011                     r = this.doc.selection.createRange();
21012                     if(r){
21013                         var target = r.parentElement();
21014                         if(!target || target.tagName.toLowerCase() != 'li'){
21015                             e.stopEvent();
21016                             r.pasteHTML('<br />');
21017                             r.collapse(false);
21018                             r.select();
21019                         }
21020                     }
21021                 }
21022                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21023                     this.cleanUpPaste.defer(100, this);
21024                     return;
21025                 }
21026                 
21027                 
21028             };
21029         }else if(Roo.isOpera){
21030             return function(e){
21031                 var k = e.getKey();
21032                 if(k == e.TAB){
21033                     e.stopEvent();
21034                     this.win.focus();
21035                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21036                     this.deferFocus();
21037                 }
21038                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21039                     this.cleanUpPaste.defer(100, this);
21040                     return;
21041                 }
21042                 
21043             };
21044         }else if(Roo.isSafari){
21045             return function(e){
21046                 var k = e.getKey();
21047                 
21048                 if(k == e.TAB){
21049                     e.stopEvent();
21050                     this.execCmd('InsertText','\t');
21051                     this.deferFocus();
21052                     return;
21053                 }
21054                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21055                     this.cleanUpPaste.defer(100, this);
21056                     return;
21057                 }
21058                 
21059              };
21060         }
21061     }(),
21062     
21063     getAllAncestors: function()
21064     {
21065         var p = this.getSelectedNode();
21066         var a = [];
21067         if (!p) {
21068             a.push(p); // push blank onto stack..
21069             p = this.getParentElement();
21070         }
21071         
21072         
21073         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21074             a.push(p);
21075             p = p.parentNode;
21076         }
21077         a.push(this.doc.body);
21078         return a;
21079     },
21080     lastSel : false,
21081     lastSelNode : false,
21082     
21083     
21084     getSelection : function() 
21085     {
21086         this.assignDocWin();
21087         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21088     },
21089     
21090     getSelectedNode: function() 
21091     {
21092         // this may only work on Gecko!!!
21093         
21094         // should we cache this!!!!
21095         
21096         
21097         
21098          
21099         var range = this.createRange(this.getSelection()).cloneRange();
21100         
21101         if (Roo.isIE) {
21102             var parent = range.parentElement();
21103             while (true) {
21104                 var testRange = range.duplicate();
21105                 testRange.moveToElementText(parent);
21106                 if (testRange.inRange(range)) {
21107                     break;
21108                 }
21109                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21110                     break;
21111                 }
21112                 parent = parent.parentElement;
21113             }
21114             return parent;
21115         }
21116         
21117         // is ancestor a text element.
21118         var ac =  range.commonAncestorContainer;
21119         if (ac.nodeType == 3) {
21120             ac = ac.parentNode;
21121         }
21122         
21123         var ar = ac.childNodes;
21124          
21125         var nodes = [];
21126         var other_nodes = [];
21127         var has_other_nodes = false;
21128         for (var i=0;i<ar.length;i++) {
21129             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21130                 continue;
21131             }
21132             // fullly contained node.
21133             
21134             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21135                 nodes.push(ar[i]);
21136                 continue;
21137             }
21138             
21139             // probably selected..
21140             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21141                 other_nodes.push(ar[i]);
21142                 continue;
21143             }
21144             // outer..
21145             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21146                 continue;
21147             }
21148             
21149             
21150             has_other_nodes = true;
21151         }
21152         if (!nodes.length && other_nodes.length) {
21153             nodes= other_nodes;
21154         }
21155         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21156             return false;
21157         }
21158         
21159         return nodes[0];
21160     },
21161     createRange: function(sel)
21162     {
21163         // this has strange effects when using with 
21164         // top toolbar - not sure if it's a great idea.
21165         //this.editor.contentWindow.focus();
21166         if (typeof sel != "undefined") {
21167             try {
21168                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21169             } catch(e) {
21170                 return this.doc.createRange();
21171             }
21172         } else {
21173             return this.doc.createRange();
21174         }
21175     },
21176     getParentElement: function()
21177     {
21178         
21179         this.assignDocWin();
21180         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21181         
21182         var range = this.createRange(sel);
21183          
21184         try {
21185             var p = range.commonAncestorContainer;
21186             while (p.nodeType == 3) { // text node
21187                 p = p.parentNode;
21188             }
21189             return p;
21190         } catch (e) {
21191             return null;
21192         }
21193     
21194     },
21195     /***
21196      *
21197      * Range intersection.. the hard stuff...
21198      *  '-1' = before
21199      *  '0' = hits..
21200      *  '1' = after.
21201      *         [ -- selected range --- ]
21202      *   [fail]                        [fail]
21203      *
21204      *    basically..
21205      *      if end is before start or  hits it. fail.
21206      *      if start is after end or hits it fail.
21207      *
21208      *   if either hits (but other is outside. - then it's not 
21209      *   
21210      *    
21211      **/
21212     
21213     
21214     // @see http://www.thismuchiknow.co.uk/?p=64.
21215     rangeIntersectsNode : function(range, node)
21216     {
21217         var nodeRange = node.ownerDocument.createRange();
21218         try {
21219             nodeRange.selectNode(node);
21220         } catch (e) {
21221             nodeRange.selectNodeContents(node);
21222         }
21223     
21224         var rangeStartRange = range.cloneRange();
21225         rangeStartRange.collapse(true);
21226     
21227         var rangeEndRange = range.cloneRange();
21228         rangeEndRange.collapse(false);
21229     
21230         var nodeStartRange = nodeRange.cloneRange();
21231         nodeStartRange.collapse(true);
21232     
21233         var nodeEndRange = nodeRange.cloneRange();
21234         nodeEndRange.collapse(false);
21235     
21236         return rangeStartRange.compareBoundaryPoints(
21237                  Range.START_TO_START, nodeEndRange) == -1 &&
21238                rangeEndRange.compareBoundaryPoints(
21239                  Range.START_TO_START, nodeStartRange) == 1;
21240         
21241          
21242     },
21243     rangeCompareNode : function(range, node)
21244     {
21245         var nodeRange = node.ownerDocument.createRange();
21246         try {
21247             nodeRange.selectNode(node);
21248         } catch (e) {
21249             nodeRange.selectNodeContents(node);
21250         }
21251         
21252         
21253         range.collapse(true);
21254     
21255         nodeRange.collapse(true);
21256      
21257         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21258         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21259          
21260         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21261         
21262         var nodeIsBefore   =  ss == 1;
21263         var nodeIsAfter    = ee == -1;
21264         
21265         if (nodeIsBefore && nodeIsAfter) {
21266             return 0; // outer
21267         }
21268         if (!nodeIsBefore && nodeIsAfter) {
21269             return 1; //right trailed.
21270         }
21271         
21272         if (nodeIsBefore && !nodeIsAfter) {
21273             return 2;  // left trailed.
21274         }
21275         // fully contined.
21276         return 3;
21277     },
21278
21279     // private? - in a new class?
21280     cleanUpPaste :  function()
21281     {
21282         // cleans up the whole document..
21283         Roo.log('cleanuppaste');
21284         
21285         this.cleanUpChildren(this.doc.body);
21286         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21287         if (clean != this.doc.body.innerHTML) {
21288             this.doc.body.innerHTML = clean;
21289         }
21290         
21291     },
21292     
21293     cleanWordChars : function(input) {// change the chars to hex code
21294         var he = Roo.HtmlEditorCore;
21295         
21296         var output = input;
21297         Roo.each(he.swapCodes, function(sw) { 
21298             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21299             
21300             output = output.replace(swapper, sw[1]);
21301         });
21302         
21303         return output;
21304     },
21305     
21306     
21307     cleanUpChildren : function (n)
21308     {
21309         if (!n.childNodes.length) {
21310             return;
21311         }
21312         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21313            this.cleanUpChild(n.childNodes[i]);
21314         }
21315     },
21316     
21317     
21318         
21319     
21320     cleanUpChild : function (node)
21321     {
21322         var ed = this;
21323         //console.log(node);
21324         if (node.nodeName == "#text") {
21325             // clean up silly Windows -- stuff?
21326             return; 
21327         }
21328         if (node.nodeName == "#comment") {
21329             node.parentNode.removeChild(node);
21330             // clean up silly Windows -- stuff?
21331             return; 
21332         }
21333         var lcname = node.tagName.toLowerCase();
21334         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21335         // whitelist of tags..
21336         
21337         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21338             // remove node.
21339             node.parentNode.removeChild(node);
21340             return;
21341             
21342         }
21343         
21344         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21345         
21346         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21347         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21348         
21349         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21350         //    remove_keep_children = true;
21351         //}
21352         
21353         if (remove_keep_children) {
21354             this.cleanUpChildren(node);
21355             // inserts everything just before this node...
21356             while (node.childNodes.length) {
21357                 var cn = node.childNodes[0];
21358                 node.removeChild(cn);
21359                 node.parentNode.insertBefore(cn, node);
21360             }
21361             node.parentNode.removeChild(node);
21362             return;
21363         }
21364         
21365         if (!node.attributes || !node.attributes.length) {
21366             this.cleanUpChildren(node);
21367             return;
21368         }
21369         
21370         function cleanAttr(n,v)
21371         {
21372             
21373             if (v.match(/^\./) || v.match(/^\//)) {
21374                 return;
21375             }
21376             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21377                 return;
21378             }
21379             if (v.match(/^#/)) {
21380                 return;
21381             }
21382 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21383             node.removeAttribute(n);
21384             
21385         }
21386         
21387         var cwhite = this.cwhite;
21388         var cblack = this.cblack;
21389             
21390         function cleanStyle(n,v)
21391         {
21392             if (v.match(/expression/)) { //XSS?? should we even bother..
21393                 node.removeAttribute(n);
21394                 return;
21395             }
21396             
21397             var parts = v.split(/;/);
21398             var clean = [];
21399             
21400             Roo.each(parts, function(p) {
21401                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21402                 if (!p.length) {
21403                     return true;
21404                 }
21405                 var l = p.split(':').shift().replace(/\s+/g,'');
21406                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21407                 
21408                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21409 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21410                     //node.removeAttribute(n);
21411                     return true;
21412                 }
21413                 //Roo.log()
21414                 // only allow 'c whitelisted system attributes'
21415                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21416 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21417                     //node.removeAttribute(n);
21418                     return true;
21419                 }
21420                 
21421                 
21422                  
21423                 
21424                 clean.push(p);
21425                 return true;
21426             });
21427             if (clean.length) { 
21428                 node.setAttribute(n, clean.join(';'));
21429             } else {
21430                 node.removeAttribute(n);
21431             }
21432             
21433         }
21434         
21435         
21436         for (var i = node.attributes.length-1; i > -1 ; i--) {
21437             var a = node.attributes[i];
21438             //console.log(a);
21439             
21440             if (a.name.toLowerCase().substr(0,2)=='on')  {
21441                 node.removeAttribute(a.name);
21442                 continue;
21443             }
21444             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21445                 node.removeAttribute(a.name);
21446                 continue;
21447             }
21448             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21449                 cleanAttr(a.name,a.value); // fixme..
21450                 continue;
21451             }
21452             if (a.name == 'style') {
21453                 cleanStyle(a.name,a.value);
21454                 continue;
21455             }
21456             /// clean up MS crap..
21457             // tecnically this should be a list of valid class'es..
21458             
21459             
21460             if (a.name == 'class') {
21461                 if (a.value.match(/^Mso/)) {
21462                     node.className = '';
21463                 }
21464                 
21465                 if (a.value.match(/body/)) {
21466                     node.className = '';
21467                 }
21468                 continue;
21469             }
21470             
21471             // style cleanup!?
21472             // class cleanup?
21473             
21474         }
21475         
21476         
21477         this.cleanUpChildren(node);
21478         
21479         
21480     },
21481     
21482     /**
21483      * Clean up MS wordisms...
21484      */
21485     cleanWord : function(node)
21486     {
21487         
21488         
21489         if (!node) {
21490             this.cleanWord(this.doc.body);
21491             return;
21492         }
21493         if (node.nodeName == "#text") {
21494             // clean up silly Windows -- stuff?
21495             return; 
21496         }
21497         if (node.nodeName == "#comment") {
21498             node.parentNode.removeChild(node);
21499             // clean up silly Windows -- stuff?
21500             return; 
21501         }
21502         
21503         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21504             node.parentNode.removeChild(node);
21505             return;
21506         }
21507         
21508         // remove - but keep children..
21509         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21510             while (node.childNodes.length) {
21511                 var cn = node.childNodes[0];
21512                 node.removeChild(cn);
21513                 node.parentNode.insertBefore(cn, node);
21514             }
21515             node.parentNode.removeChild(node);
21516             this.iterateChildren(node, this.cleanWord);
21517             return;
21518         }
21519         // clean styles
21520         if (node.className.length) {
21521             
21522             var cn = node.className.split(/\W+/);
21523             var cna = [];
21524             Roo.each(cn, function(cls) {
21525                 if (cls.match(/Mso[a-zA-Z]+/)) {
21526                     return;
21527                 }
21528                 cna.push(cls);
21529             });
21530             node.className = cna.length ? cna.join(' ') : '';
21531             if (!cna.length) {
21532                 node.removeAttribute("class");
21533             }
21534         }
21535         
21536         if (node.hasAttribute("lang")) {
21537             node.removeAttribute("lang");
21538         }
21539         
21540         if (node.hasAttribute("style")) {
21541             
21542             var styles = node.getAttribute("style").split(";");
21543             var nstyle = [];
21544             Roo.each(styles, function(s) {
21545                 if (!s.match(/:/)) {
21546                     return;
21547                 }
21548                 var kv = s.split(":");
21549                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21550                     return;
21551                 }
21552                 // what ever is left... we allow.
21553                 nstyle.push(s);
21554             });
21555             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21556             if (!nstyle.length) {
21557                 node.removeAttribute('style');
21558             }
21559         }
21560         this.iterateChildren(node, this.cleanWord);
21561         
21562         
21563         
21564     },
21565     /**
21566      * iterateChildren of a Node, calling fn each time, using this as the scole..
21567      * @param {DomNode} node node to iterate children of.
21568      * @param {Function} fn method of this class to call on each item.
21569      */
21570     iterateChildren : function(node, fn)
21571     {
21572         if (!node.childNodes.length) {
21573                 return;
21574         }
21575         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21576            fn.call(this, node.childNodes[i])
21577         }
21578     },
21579     
21580     
21581     /**
21582      * cleanTableWidths.
21583      *
21584      * Quite often pasting from word etc.. results in tables with column and widths.
21585      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21586      *
21587      */
21588     cleanTableWidths : function(node)
21589     {
21590          
21591          
21592         if (!node) {
21593             this.cleanTableWidths(this.doc.body);
21594             return;
21595         }
21596         
21597         // ignore list...
21598         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21599             return; 
21600         }
21601         Roo.log(node.tagName);
21602         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21603             this.iterateChildren(node, this.cleanTableWidths);
21604             return;
21605         }
21606         if (node.hasAttribute('width')) {
21607             node.removeAttribute('width');
21608         }
21609         
21610          
21611         if (node.hasAttribute("style")) {
21612             // pretty basic...
21613             
21614             var styles = node.getAttribute("style").split(";");
21615             var nstyle = [];
21616             Roo.each(styles, function(s) {
21617                 if (!s.match(/:/)) {
21618                     return;
21619                 }
21620                 var kv = s.split(":");
21621                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21622                     return;
21623                 }
21624                 // what ever is left... we allow.
21625                 nstyle.push(s);
21626             });
21627             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21628             if (!nstyle.length) {
21629                 node.removeAttribute('style');
21630             }
21631         }
21632         
21633         this.iterateChildren(node, this.cleanTableWidths);
21634         
21635         
21636     },
21637     
21638     
21639     
21640     
21641     domToHTML : function(currentElement, depth, nopadtext) {
21642         
21643         depth = depth || 0;
21644         nopadtext = nopadtext || false;
21645     
21646         if (!currentElement) {
21647             return this.domToHTML(this.doc.body);
21648         }
21649         
21650         //Roo.log(currentElement);
21651         var j;
21652         var allText = false;
21653         var nodeName = currentElement.nodeName;
21654         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21655         
21656         if  (nodeName == '#text') {
21657             
21658             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21659         }
21660         
21661         
21662         var ret = '';
21663         if (nodeName != 'BODY') {
21664              
21665             var i = 0;
21666             // Prints the node tagName, such as <A>, <IMG>, etc
21667             if (tagName) {
21668                 var attr = [];
21669                 for(i = 0; i < currentElement.attributes.length;i++) {
21670                     // quoting?
21671                     var aname = currentElement.attributes.item(i).name;
21672                     if (!currentElement.attributes.item(i).value.length) {
21673                         continue;
21674                     }
21675                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21676                 }
21677                 
21678                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21679             } 
21680             else {
21681                 
21682                 // eack
21683             }
21684         } else {
21685             tagName = false;
21686         }
21687         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21688             return ret;
21689         }
21690         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21691             nopadtext = true;
21692         }
21693         
21694         
21695         // Traverse the tree
21696         i = 0;
21697         var currentElementChild = currentElement.childNodes.item(i);
21698         var allText = true;
21699         var innerHTML  = '';
21700         lastnode = '';
21701         while (currentElementChild) {
21702             // Formatting code (indent the tree so it looks nice on the screen)
21703             var nopad = nopadtext;
21704             if (lastnode == 'SPAN') {
21705                 nopad  = true;
21706             }
21707             // text
21708             if  (currentElementChild.nodeName == '#text') {
21709                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21710                 toadd = nopadtext ? toadd : toadd.trim();
21711                 if (!nopad && toadd.length > 80) {
21712                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21713                 }
21714                 innerHTML  += toadd;
21715                 
21716                 i++;
21717                 currentElementChild = currentElement.childNodes.item(i);
21718                 lastNode = '';
21719                 continue;
21720             }
21721             allText = false;
21722             
21723             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21724                 
21725             // Recursively traverse the tree structure of the child node
21726             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21727             lastnode = currentElementChild.nodeName;
21728             i++;
21729             currentElementChild=currentElement.childNodes.item(i);
21730         }
21731         
21732         ret += innerHTML;
21733         
21734         if (!allText) {
21735                 // The remaining code is mostly for formatting the tree
21736             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21737         }
21738         
21739         
21740         if (tagName) {
21741             ret+= "</"+tagName+">";
21742         }
21743         return ret;
21744         
21745     },
21746         
21747     applyBlacklists : function()
21748     {
21749         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21750         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21751         
21752         this.white = [];
21753         this.black = [];
21754         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21755             if (b.indexOf(tag) > -1) {
21756                 return;
21757             }
21758             this.white.push(tag);
21759             
21760         }, this);
21761         
21762         Roo.each(w, function(tag) {
21763             if (b.indexOf(tag) > -1) {
21764                 return;
21765             }
21766             if (this.white.indexOf(tag) > -1) {
21767                 return;
21768             }
21769             this.white.push(tag);
21770             
21771         }, this);
21772         
21773         
21774         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21775             if (w.indexOf(tag) > -1) {
21776                 return;
21777             }
21778             this.black.push(tag);
21779             
21780         }, this);
21781         
21782         Roo.each(b, function(tag) {
21783             if (w.indexOf(tag) > -1) {
21784                 return;
21785             }
21786             if (this.black.indexOf(tag) > -1) {
21787                 return;
21788             }
21789             this.black.push(tag);
21790             
21791         }, this);
21792         
21793         
21794         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21795         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21796         
21797         this.cwhite = [];
21798         this.cblack = [];
21799         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21800             if (b.indexOf(tag) > -1) {
21801                 return;
21802             }
21803             this.cwhite.push(tag);
21804             
21805         }, this);
21806         
21807         Roo.each(w, function(tag) {
21808             if (b.indexOf(tag) > -1) {
21809                 return;
21810             }
21811             if (this.cwhite.indexOf(tag) > -1) {
21812                 return;
21813             }
21814             this.cwhite.push(tag);
21815             
21816         }, this);
21817         
21818         
21819         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21820             if (w.indexOf(tag) > -1) {
21821                 return;
21822             }
21823             this.cblack.push(tag);
21824             
21825         }, this);
21826         
21827         Roo.each(b, function(tag) {
21828             if (w.indexOf(tag) > -1) {
21829                 return;
21830             }
21831             if (this.cblack.indexOf(tag) > -1) {
21832                 return;
21833             }
21834             this.cblack.push(tag);
21835             
21836         }, this);
21837     },
21838     
21839     setStylesheets : function(stylesheets)
21840     {
21841         if(typeof(stylesheets) == 'string'){
21842             Roo.get(this.iframe.contentDocument.head).createChild({
21843                 tag : 'link',
21844                 rel : 'stylesheet',
21845                 type : 'text/css',
21846                 href : stylesheets
21847             });
21848             
21849             return;
21850         }
21851         var _this = this;
21852      
21853         Roo.each(stylesheets, function(s) {
21854             if(!s.length){
21855                 return;
21856             }
21857             
21858             Roo.get(_this.iframe.contentDocument.head).createChild({
21859                 tag : 'link',
21860                 rel : 'stylesheet',
21861                 type : 'text/css',
21862                 href : s
21863             });
21864         });
21865
21866         
21867     },
21868     
21869     removeStylesheets : function()
21870     {
21871         var _this = this;
21872         
21873         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21874             s.remove();
21875         });
21876     }
21877     
21878     // hide stuff that is not compatible
21879     /**
21880      * @event blur
21881      * @hide
21882      */
21883     /**
21884      * @event change
21885      * @hide
21886      */
21887     /**
21888      * @event focus
21889      * @hide
21890      */
21891     /**
21892      * @event specialkey
21893      * @hide
21894      */
21895     /**
21896      * @cfg {String} fieldClass @hide
21897      */
21898     /**
21899      * @cfg {String} focusClass @hide
21900      */
21901     /**
21902      * @cfg {String} autoCreate @hide
21903      */
21904     /**
21905      * @cfg {String} inputType @hide
21906      */
21907     /**
21908      * @cfg {String} invalidClass @hide
21909      */
21910     /**
21911      * @cfg {String} invalidText @hide
21912      */
21913     /**
21914      * @cfg {String} msgFx @hide
21915      */
21916     /**
21917      * @cfg {String} validateOnBlur @hide
21918      */
21919 });
21920
21921 Roo.HtmlEditorCore.white = [
21922         'area', 'br', 'img', 'input', 'hr', 'wbr',
21923         
21924        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21925        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21926        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21927        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21928        'table',   'ul',         'xmp', 
21929        
21930        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21931       'thead',   'tr', 
21932      
21933       'dir', 'menu', 'ol', 'ul', 'dl',
21934        
21935       'embed',  'object'
21936 ];
21937
21938
21939 Roo.HtmlEditorCore.black = [
21940     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21941         'applet', // 
21942         'base',   'basefont', 'bgsound', 'blink',  'body', 
21943         'frame',  'frameset', 'head',    'html',   'ilayer', 
21944         'iframe', 'layer',  'link',     'meta',    'object',   
21945         'script', 'style' ,'title',  'xml' // clean later..
21946 ];
21947 Roo.HtmlEditorCore.clean = [
21948     'script', 'style', 'title', 'xml'
21949 ];
21950 Roo.HtmlEditorCore.remove = [
21951     'font'
21952 ];
21953 // attributes..
21954
21955 Roo.HtmlEditorCore.ablack = [
21956     'on'
21957 ];
21958     
21959 Roo.HtmlEditorCore.aclean = [ 
21960     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
21961 ];
21962
21963 // protocols..
21964 Roo.HtmlEditorCore.pwhite= [
21965         'http',  'https',  'mailto'
21966 ];
21967
21968 // white listed style attributes.
21969 Roo.HtmlEditorCore.cwhite= [
21970       //  'text-align', /// default is to allow most things..
21971       
21972          
21973 //        'font-size'//??
21974 ];
21975
21976 // black listed style attributes.
21977 Roo.HtmlEditorCore.cblack= [
21978       //  'font-size' -- this can be set by the project 
21979 ];
21980
21981
21982 Roo.HtmlEditorCore.swapCodes   =[ 
21983     [    8211, "--" ], 
21984     [    8212, "--" ], 
21985     [    8216,  "'" ],  
21986     [    8217, "'" ],  
21987     [    8220, '"' ],  
21988     [    8221, '"' ],  
21989     [    8226, "*" ],  
21990     [    8230, "..." ]
21991 ]; 
21992
21993     /*
21994  * - LGPL
21995  *
21996  * HtmlEditor
21997  * 
21998  */
21999
22000 /**
22001  * @class Roo.bootstrap.HtmlEditor
22002  * @extends Roo.bootstrap.TextArea
22003  * Bootstrap HtmlEditor class
22004
22005  * @constructor
22006  * Create a new HtmlEditor
22007  * @param {Object} config The config object
22008  */
22009
22010 Roo.bootstrap.HtmlEditor = function(config){
22011     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22012     if (!this.toolbars) {
22013         this.toolbars = [];
22014     }
22015     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22016     this.addEvents({
22017             /**
22018              * @event initialize
22019              * Fires when the editor is fully initialized (including the iframe)
22020              * @param {HtmlEditor} this
22021              */
22022             initialize: true,
22023             /**
22024              * @event activate
22025              * Fires when the editor is first receives the focus. Any insertion must wait
22026              * until after this event.
22027              * @param {HtmlEditor} this
22028              */
22029             activate: true,
22030              /**
22031              * @event beforesync
22032              * Fires before the textarea is updated with content from the editor iframe. Return false
22033              * to cancel the sync.
22034              * @param {HtmlEditor} this
22035              * @param {String} html
22036              */
22037             beforesync: true,
22038              /**
22039              * @event beforepush
22040              * Fires before the iframe editor is updated with content from the textarea. Return false
22041              * to cancel the push.
22042              * @param {HtmlEditor} this
22043              * @param {String} html
22044              */
22045             beforepush: true,
22046              /**
22047              * @event sync
22048              * Fires when the textarea is updated with content from the editor iframe.
22049              * @param {HtmlEditor} this
22050              * @param {String} html
22051              */
22052             sync: true,
22053              /**
22054              * @event push
22055              * Fires when the iframe editor is updated with content from the textarea.
22056              * @param {HtmlEditor} this
22057              * @param {String} html
22058              */
22059             push: true,
22060              /**
22061              * @event editmodechange
22062              * Fires when the editor switches edit modes
22063              * @param {HtmlEditor} this
22064              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22065              */
22066             editmodechange: true,
22067             /**
22068              * @event editorevent
22069              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22070              * @param {HtmlEditor} this
22071              */
22072             editorevent: true,
22073             /**
22074              * @event firstfocus
22075              * Fires when on first focus - needed by toolbars..
22076              * @param {HtmlEditor} this
22077              */
22078             firstfocus: true,
22079             /**
22080              * @event autosave
22081              * Auto save the htmlEditor value as a file into Events
22082              * @param {HtmlEditor} this
22083              */
22084             autosave: true,
22085             /**
22086              * @event savedpreview
22087              * preview the saved version of htmlEditor
22088              * @param {HtmlEditor} this
22089              */
22090             savedpreview: true
22091         });
22092 };
22093
22094
22095 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22096     
22097     
22098       /**
22099      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22100      */
22101     toolbars : false,
22102    
22103      /**
22104      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22105      *                        Roo.resizable.
22106      */
22107     resizable : false,
22108      /**
22109      * @cfg {Number} height (in pixels)
22110      */   
22111     height: 300,
22112    /**
22113      * @cfg {Number} width (in pixels)
22114      */   
22115     width: false,
22116     
22117     /**
22118      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22119      * 
22120      */
22121     stylesheets: false,
22122     
22123     // id of frame..
22124     frameId: false,
22125     
22126     // private properties
22127     validationEvent : false,
22128     deferHeight: true,
22129     initialized : false,
22130     activated : false,
22131     
22132     onFocus : Roo.emptyFn,
22133     iframePad:3,
22134     hideMode:'offsets',
22135     
22136     
22137     tbContainer : false,
22138     
22139     toolbarContainer :function() {
22140         return this.wrap.select('.x-html-editor-tb',true).first();
22141     },
22142
22143     /**
22144      * Protected method that will not generally be called directly. It
22145      * is called when the editor creates its toolbar. Override this method if you need to
22146      * add custom toolbar buttons.
22147      * @param {HtmlEditor} editor
22148      */
22149     createToolbar : function(){
22150         
22151         Roo.log("create toolbars");
22152         
22153         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22154         this.toolbars[0].render(this.toolbarContainer());
22155         
22156         return;
22157         
22158 //        if (!editor.toolbars || !editor.toolbars.length) {
22159 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22160 //        }
22161 //        
22162 //        for (var i =0 ; i < editor.toolbars.length;i++) {
22163 //            editor.toolbars[i] = Roo.factory(
22164 //                    typeof(editor.toolbars[i]) == 'string' ?
22165 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
22166 //                Roo.bootstrap.HtmlEditor);
22167 //            editor.toolbars[i].init(editor);
22168 //        }
22169     },
22170
22171      
22172     // private
22173     onRender : function(ct, position)
22174     {
22175        // Roo.log("Call onRender: " + this.xtype);
22176         var _t = this;
22177         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22178       
22179         this.wrap = this.inputEl().wrap({
22180             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22181         });
22182         
22183         this.editorcore.onRender(ct, position);
22184          
22185         if (this.resizable) {
22186             this.resizeEl = new Roo.Resizable(this.wrap, {
22187                 pinned : true,
22188                 wrap: true,
22189                 dynamic : true,
22190                 minHeight : this.height,
22191                 height: this.height,
22192                 handles : this.resizable,
22193                 width: this.width,
22194                 listeners : {
22195                     resize : function(r, w, h) {
22196                         _t.onResize(w,h); // -something
22197                     }
22198                 }
22199             });
22200             
22201         }
22202         this.createToolbar(this);
22203        
22204         
22205         if(!this.width && this.resizable){
22206             this.setSize(this.wrap.getSize());
22207         }
22208         if (this.resizeEl) {
22209             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22210             // should trigger onReize..
22211         }
22212         
22213     },
22214
22215     // private
22216     onResize : function(w, h)
22217     {
22218         Roo.log('resize: ' +w + ',' + h );
22219         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22220         var ew = false;
22221         var eh = false;
22222         
22223         if(this.inputEl() ){
22224             if(typeof w == 'number'){
22225                 var aw = w - this.wrap.getFrameWidth('lr');
22226                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22227                 ew = aw;
22228             }
22229             if(typeof h == 'number'){
22230                  var tbh = -11;  // fixme it needs to tool bar size!
22231                 for (var i =0; i < this.toolbars.length;i++) {
22232                     // fixme - ask toolbars for heights?
22233                     tbh += this.toolbars[i].el.getHeight();
22234                     //if (this.toolbars[i].footer) {
22235                     //    tbh += this.toolbars[i].footer.el.getHeight();
22236                     //}
22237                 }
22238               
22239                 
22240                 
22241                 
22242                 
22243                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22244                 ah -= 5; // knock a few pixes off for look..
22245                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
22246                 var eh = ah;
22247             }
22248         }
22249         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22250         this.editorcore.onResize(ew,eh);
22251         
22252     },
22253
22254     /**
22255      * Toggles the editor between standard and source edit mode.
22256      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22257      */
22258     toggleSourceEdit : function(sourceEditMode)
22259     {
22260         this.editorcore.toggleSourceEdit(sourceEditMode);
22261         
22262         if(this.editorcore.sourceEditMode){
22263             Roo.log('editor - showing textarea');
22264             
22265 //            Roo.log('in');
22266 //            Roo.log(this.syncValue());
22267             this.syncValue();
22268             this.inputEl().removeClass(['hide', 'x-hidden']);
22269             this.inputEl().dom.removeAttribute('tabIndex');
22270             this.inputEl().focus();
22271         }else{
22272             Roo.log('editor - hiding textarea');
22273 //            Roo.log('out')
22274 //            Roo.log(this.pushValue()); 
22275             this.pushValue();
22276             
22277             this.inputEl().addClass(['hide', 'x-hidden']);
22278             this.inputEl().dom.setAttribute('tabIndex', -1);
22279             //this.deferFocus();
22280         }
22281          
22282         if(this.resizable){
22283             this.setSize(this.wrap.getSize());
22284         }
22285         
22286         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22287     },
22288  
22289     // private (for BoxComponent)
22290     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22291
22292     // private (for BoxComponent)
22293     getResizeEl : function(){
22294         return this.wrap;
22295     },
22296
22297     // private (for BoxComponent)
22298     getPositionEl : function(){
22299         return this.wrap;
22300     },
22301
22302     // private
22303     initEvents : function(){
22304         this.originalValue = this.getValue();
22305     },
22306
22307 //    /**
22308 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22309 //     * @method
22310 //     */
22311 //    markInvalid : Roo.emptyFn,
22312 //    /**
22313 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22314 //     * @method
22315 //     */
22316 //    clearInvalid : Roo.emptyFn,
22317
22318     setValue : function(v){
22319         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
22320         this.editorcore.pushValue();
22321     },
22322
22323      
22324     // private
22325     deferFocus : function(){
22326         this.focus.defer(10, this);
22327     },
22328
22329     // doc'ed in Field
22330     focus : function(){
22331         this.editorcore.focus();
22332         
22333     },
22334       
22335
22336     // private
22337     onDestroy : function(){
22338         
22339         
22340         
22341         if(this.rendered){
22342             
22343             for (var i =0; i < this.toolbars.length;i++) {
22344                 // fixme - ask toolbars for heights?
22345                 this.toolbars[i].onDestroy();
22346             }
22347             
22348             this.wrap.dom.innerHTML = '';
22349             this.wrap.remove();
22350         }
22351     },
22352
22353     // private
22354     onFirstFocus : function(){
22355         //Roo.log("onFirstFocus");
22356         this.editorcore.onFirstFocus();
22357          for (var i =0; i < this.toolbars.length;i++) {
22358             this.toolbars[i].onFirstFocus();
22359         }
22360         
22361     },
22362     
22363     // private
22364     syncValue : function()
22365     {   
22366         this.editorcore.syncValue();
22367     },
22368     
22369     pushValue : function()
22370     {   
22371         this.editorcore.pushValue();
22372     }
22373      
22374     
22375     // hide stuff that is not compatible
22376     /**
22377      * @event blur
22378      * @hide
22379      */
22380     /**
22381      * @event change
22382      * @hide
22383      */
22384     /**
22385      * @event focus
22386      * @hide
22387      */
22388     /**
22389      * @event specialkey
22390      * @hide
22391      */
22392     /**
22393      * @cfg {String} fieldClass @hide
22394      */
22395     /**
22396      * @cfg {String} focusClass @hide
22397      */
22398     /**
22399      * @cfg {String} autoCreate @hide
22400      */
22401     /**
22402      * @cfg {String} inputType @hide
22403      */
22404     /**
22405      * @cfg {String} invalidClass @hide
22406      */
22407     /**
22408      * @cfg {String} invalidText @hide
22409      */
22410     /**
22411      * @cfg {String} msgFx @hide
22412      */
22413     /**
22414      * @cfg {String} validateOnBlur @hide
22415      */
22416 });
22417  
22418     
22419    
22420    
22421    
22422       
22423 Roo.namespace('Roo.bootstrap.htmleditor');
22424 /**
22425  * @class Roo.bootstrap.HtmlEditorToolbar1
22426  * Basic Toolbar
22427  * 
22428  * Usage:
22429  *
22430  new Roo.bootstrap.HtmlEditor({
22431     ....
22432     toolbars : [
22433         new Roo.bootstrap.HtmlEditorToolbar1({
22434             disable : { fonts: 1 , format: 1, ..., ... , ...],
22435             btns : [ .... ]
22436         })
22437     }
22438      
22439  * 
22440  * @cfg {Object} disable List of elements to disable..
22441  * @cfg {Array} btns List of additional buttons.
22442  * 
22443  * 
22444  * NEEDS Extra CSS? 
22445  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22446  */
22447  
22448 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22449 {
22450     
22451     Roo.apply(this, config);
22452     
22453     // default disabled, based on 'good practice'..
22454     this.disable = this.disable || {};
22455     Roo.applyIf(this.disable, {
22456         fontSize : true,
22457         colors : true,
22458         specialElements : true
22459     });
22460     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22461     
22462     this.editor = config.editor;
22463     this.editorcore = config.editor.editorcore;
22464     
22465     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22466     
22467     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22468     // dont call parent... till later.
22469 }
22470 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
22471      
22472     bar : true,
22473     
22474     editor : false,
22475     editorcore : false,
22476     
22477     
22478     formats : [
22479         "p" ,  
22480         "h1","h2","h3","h4","h5","h6", 
22481         "pre", "code", 
22482         "abbr", "acronym", "address", "cite", "samp", "var",
22483         'div','span'
22484     ],
22485     
22486     onRender : function(ct, position)
22487     {
22488        // Roo.log("Call onRender: " + this.xtype);
22489         
22490        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22491        Roo.log(this.el);
22492        this.el.dom.style.marginBottom = '0';
22493        var _this = this;
22494        var editorcore = this.editorcore;
22495        var editor= this.editor;
22496        
22497        var children = [];
22498        var btn = function(id,cmd , toggle, handler){
22499        
22500             var  event = toggle ? 'toggle' : 'click';
22501        
22502             var a = {
22503                 size : 'sm',
22504                 xtype: 'Button',
22505                 xns: Roo.bootstrap,
22506                 glyphicon : id,
22507                 cmd : id || cmd,
22508                 enableToggle:toggle !== false,
22509                 //html : 'submit'
22510                 pressed : toggle ? false : null,
22511                 listeners : {}
22512             };
22513             a.listeners[toggle ? 'toggle' : 'click'] = function() {
22514                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
22515             };
22516             children.push(a);
22517             return a;
22518        }
22519         
22520         var style = {
22521                 xtype: 'Button',
22522                 size : 'sm',
22523                 xns: Roo.bootstrap,
22524                 glyphicon : 'font',
22525                 //html : 'submit'
22526                 menu : {
22527                     xtype: 'Menu',
22528                     xns: Roo.bootstrap,
22529                     items:  []
22530                 }
22531         };
22532         Roo.each(this.formats, function(f) {
22533             style.menu.items.push({
22534                 xtype :'MenuItem',
22535                 xns: Roo.bootstrap,
22536                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22537                 tagname : f,
22538                 listeners : {
22539                     click : function()
22540                     {
22541                         editorcore.insertTag(this.tagname);
22542                         editor.focus();
22543                     }
22544                 }
22545                 
22546             });
22547         });
22548          children.push(style);   
22549             
22550             
22551         btn('bold',false,true);
22552         btn('italic',false,true);
22553         btn('align-left', 'justifyleft',true);
22554         btn('align-center', 'justifycenter',true);
22555         btn('align-right' , 'justifyright',true);
22556         btn('link', false, false, function(btn) {
22557             //Roo.log("create link?");
22558             var url = prompt(this.createLinkText, this.defaultLinkValue);
22559             if(url && url != 'http:/'+'/'){
22560                 this.editorcore.relayCmd('createlink', url);
22561             }
22562         }),
22563         btn('list','insertunorderedlist',true);
22564         btn('pencil', false,true, function(btn){
22565                 Roo.log(this);
22566                 
22567                 this.toggleSourceEdit(btn.pressed);
22568         });
22569         /*
22570         var cog = {
22571                 xtype: 'Button',
22572                 size : 'sm',
22573                 xns: Roo.bootstrap,
22574                 glyphicon : 'cog',
22575                 //html : 'submit'
22576                 menu : {
22577                     xtype: 'Menu',
22578                     xns: Roo.bootstrap,
22579                     items:  []
22580                 }
22581         };
22582         
22583         cog.menu.items.push({
22584             xtype :'MenuItem',
22585             xns: Roo.bootstrap,
22586             html : Clean styles,
22587             tagname : f,
22588             listeners : {
22589                 click : function()
22590                 {
22591                     editorcore.insertTag(this.tagname);
22592                     editor.focus();
22593                 }
22594             }
22595             
22596         });
22597        */
22598         
22599          
22600        this.xtype = 'NavSimplebar';
22601         
22602         for(var i=0;i< children.length;i++) {
22603             
22604             this.buttons.add(this.addxtypeChild(children[i]));
22605             
22606         }
22607         
22608         editor.on('editorevent', this.updateToolbar, this);
22609     },
22610     onBtnClick : function(id)
22611     {
22612        this.editorcore.relayCmd(id);
22613        this.editorcore.focus();
22614     },
22615     
22616     /**
22617      * Protected method that will not generally be called directly. It triggers
22618      * a toolbar update by reading the markup state of the current selection in the editor.
22619      */
22620     updateToolbar: function(){
22621
22622         if(!this.editorcore.activated){
22623             this.editor.onFirstFocus(); // is this neeed?
22624             return;
22625         }
22626
22627         var btns = this.buttons; 
22628         var doc = this.editorcore.doc;
22629         btns.get('bold').setActive(doc.queryCommandState('bold'));
22630         btns.get('italic').setActive(doc.queryCommandState('italic'));
22631         //btns.get('underline').setActive(doc.queryCommandState('underline'));
22632         
22633         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22634         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22635         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22636         
22637         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22638         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22639          /*
22640         
22641         var ans = this.editorcore.getAllAncestors();
22642         if (this.formatCombo) {
22643             
22644             
22645             var store = this.formatCombo.store;
22646             this.formatCombo.setValue("");
22647             for (var i =0; i < ans.length;i++) {
22648                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22649                     // select it..
22650                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22651                     break;
22652                 }
22653             }
22654         }
22655         
22656         
22657         
22658         // hides menus... - so this cant be on a menu...
22659         Roo.bootstrap.MenuMgr.hideAll();
22660         */
22661         Roo.bootstrap.MenuMgr.hideAll();
22662         //this.editorsyncValue();
22663     },
22664     onFirstFocus: function() {
22665         this.buttons.each(function(item){
22666            item.enable();
22667         });
22668     },
22669     toggleSourceEdit : function(sourceEditMode){
22670         
22671           
22672         if(sourceEditMode){
22673             Roo.log("disabling buttons");
22674            this.buttons.each( function(item){
22675                 if(item.cmd != 'pencil'){
22676                     item.disable();
22677                 }
22678             });
22679           
22680         }else{
22681             Roo.log("enabling buttons");
22682             if(this.editorcore.initialized){
22683                 this.buttons.each( function(item){
22684                     item.enable();
22685                 });
22686             }
22687             
22688         }
22689         Roo.log("calling toggole on editor");
22690         // tell the editor that it's been pressed..
22691         this.editor.toggleSourceEdit(sourceEditMode);
22692        
22693     }
22694 });
22695
22696
22697
22698
22699
22700 /**
22701  * @class Roo.bootstrap.Table.AbstractSelectionModel
22702  * @extends Roo.util.Observable
22703  * Abstract base class for grid SelectionModels.  It provides the interface that should be
22704  * implemented by descendant classes.  This class should not be directly instantiated.
22705  * @constructor
22706  */
22707 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22708     this.locked = false;
22709     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22710 };
22711
22712
22713 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
22714     /** @ignore Called by the grid automatically. Do not call directly. */
22715     init : function(grid){
22716         this.grid = grid;
22717         this.initEvents();
22718     },
22719
22720     /**
22721      * Locks the selections.
22722      */
22723     lock : function(){
22724         this.locked = true;
22725     },
22726
22727     /**
22728      * Unlocks the selections.
22729      */
22730     unlock : function(){
22731         this.locked = false;
22732     },
22733
22734     /**
22735      * Returns true if the selections are locked.
22736      * @return {Boolean}
22737      */
22738     isLocked : function(){
22739         return this.locked;
22740     }
22741 });
22742 /**
22743  * @extends Roo.bootstrap.Table.AbstractSelectionModel
22744  * @class Roo.bootstrap.Table.RowSelectionModel
22745  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22746  * It supports multiple selections and keyboard selection/navigation. 
22747  * @constructor
22748  * @param {Object} config
22749  */
22750
22751 Roo.bootstrap.Table.RowSelectionModel = function(config){
22752     Roo.apply(this, config);
22753     this.selections = new Roo.util.MixedCollection(false, function(o){
22754         return o.id;
22755     });
22756
22757     this.last = false;
22758     this.lastActive = false;
22759
22760     this.addEvents({
22761         /**
22762              * @event selectionchange
22763              * Fires when the selection changes
22764              * @param {SelectionModel} this
22765              */
22766             "selectionchange" : true,
22767         /**
22768              * @event afterselectionchange
22769              * Fires after the selection changes (eg. by key press or clicking)
22770              * @param {SelectionModel} this
22771              */
22772             "afterselectionchange" : true,
22773         /**
22774              * @event beforerowselect
22775              * Fires when a row is selected being selected, return false to cancel.
22776              * @param {SelectionModel} this
22777              * @param {Number} rowIndex The selected index
22778              * @param {Boolean} keepExisting False if other selections will be cleared
22779              */
22780             "beforerowselect" : true,
22781         /**
22782              * @event rowselect
22783              * Fires when a row is selected.
22784              * @param {SelectionModel} this
22785              * @param {Number} rowIndex The selected index
22786              * @param {Roo.data.Record} r The record
22787              */
22788             "rowselect" : true,
22789         /**
22790              * @event rowdeselect
22791              * Fires when a row is deselected.
22792              * @param {SelectionModel} this
22793              * @param {Number} rowIndex The selected index
22794              */
22795         "rowdeselect" : true
22796     });
22797     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22798     this.locked = false;
22799  };
22800
22801 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
22802     /**
22803      * @cfg {Boolean} singleSelect
22804      * True to allow selection of only one row at a time (defaults to false)
22805      */
22806     singleSelect : false,
22807
22808     // private
22809     initEvents : function()
22810     {
22811
22812         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22813         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
22814         //}else{ // allow click to work like normal
22815          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
22816         //}
22817         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
22818         this.grid.on("rowclick", this.handleMouseDown, this);
22819         
22820         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22821             "up" : function(e){
22822                 if(!e.shiftKey){
22823                     this.selectPrevious(e.shiftKey);
22824                 }else if(this.last !== false && this.lastActive !== false){
22825                     var last = this.last;
22826                     this.selectRange(this.last,  this.lastActive-1);
22827                     this.grid.getView().focusRow(this.lastActive);
22828                     if(last !== false){
22829                         this.last = last;
22830                     }
22831                 }else{
22832                     this.selectFirstRow();
22833                 }
22834                 this.fireEvent("afterselectionchange", this);
22835             },
22836             "down" : function(e){
22837                 if(!e.shiftKey){
22838                     this.selectNext(e.shiftKey);
22839                 }else if(this.last !== false && this.lastActive !== false){
22840                     var last = this.last;
22841                     this.selectRange(this.last,  this.lastActive+1);
22842                     this.grid.getView().focusRow(this.lastActive);
22843                     if(last !== false){
22844                         this.last = last;
22845                     }
22846                 }else{
22847                     this.selectFirstRow();
22848                 }
22849                 this.fireEvent("afterselectionchange", this);
22850             },
22851             scope: this
22852         });
22853         this.grid.store.on('load', function(){
22854             this.selections.clear();
22855         },this);
22856         /*
22857         var view = this.grid.view;
22858         view.on("refresh", this.onRefresh, this);
22859         view.on("rowupdated", this.onRowUpdated, this);
22860         view.on("rowremoved", this.onRemove, this);
22861         */
22862     },
22863
22864     // private
22865     onRefresh : function()
22866     {
22867         var ds = this.grid.store, i, v = this.grid.view;
22868         var s = this.selections;
22869         s.each(function(r){
22870             if((i = ds.indexOfId(r.id)) != -1){
22871                 v.onRowSelect(i);
22872             }else{
22873                 s.remove(r);
22874             }
22875         });
22876     },
22877
22878     // private
22879     onRemove : function(v, index, r){
22880         this.selections.remove(r);
22881     },
22882
22883     // private
22884     onRowUpdated : function(v, index, r){
22885         if(this.isSelected(r)){
22886             v.onRowSelect(index);
22887         }
22888     },
22889
22890     /**
22891      * Select records.
22892      * @param {Array} records The records to select
22893      * @param {Boolean} keepExisting (optional) True to keep existing selections
22894      */
22895     selectRecords : function(records, keepExisting)
22896     {
22897         if(!keepExisting){
22898             this.clearSelections();
22899         }
22900             var ds = this.grid.store;
22901         for(var i = 0, len = records.length; i < len; i++){
22902             this.selectRow(ds.indexOf(records[i]), true);
22903         }
22904     },
22905
22906     /**
22907      * Gets the number of selected rows.
22908      * @return {Number}
22909      */
22910     getCount : function(){
22911         return this.selections.length;
22912     },
22913
22914     /**
22915      * Selects the first row in the grid.
22916      */
22917     selectFirstRow : function(){
22918         this.selectRow(0);
22919     },
22920
22921     /**
22922      * Select the last row.
22923      * @param {Boolean} keepExisting (optional) True to keep existing selections
22924      */
22925     selectLastRow : function(keepExisting){
22926         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22927         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
22928     },
22929
22930     /**
22931      * Selects the row immediately following the last selected row.
22932      * @param {Boolean} keepExisting (optional) True to keep existing selections
22933      */
22934     selectNext : function(keepExisting)
22935     {
22936             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
22937             this.selectRow(this.last+1, keepExisting);
22938             this.grid.getView().focusRow(this.last);
22939         }
22940     },
22941
22942     /**
22943      * Selects the row that precedes the last selected row.
22944      * @param {Boolean} keepExisting (optional) True to keep existing selections
22945      */
22946     selectPrevious : function(keepExisting){
22947         if(this.last){
22948             this.selectRow(this.last-1, keepExisting);
22949             this.grid.getView().focusRow(this.last);
22950         }
22951     },
22952
22953     /**
22954      * Returns the selected records
22955      * @return {Array} Array of selected records
22956      */
22957     getSelections : function(){
22958         return [].concat(this.selections.items);
22959     },
22960
22961     /**
22962      * Returns the first selected record.
22963      * @return {Record}
22964      */
22965     getSelected : function(){
22966         return this.selections.itemAt(0);
22967     },
22968
22969
22970     /**
22971      * Clears all selections.
22972      */
22973     clearSelections : function(fast)
22974     {
22975         if(this.locked) {
22976             return;
22977         }
22978         if(fast !== true){
22979                 var ds = this.grid.store;
22980             var s = this.selections;
22981             s.each(function(r){
22982                 this.deselectRow(ds.indexOfId(r.id));
22983             }, this);
22984             s.clear();
22985         }else{
22986             this.selections.clear();
22987         }
22988         this.last = false;
22989     },
22990
22991
22992     /**
22993      * Selects all rows.
22994      */
22995     selectAll : function(){
22996         if(this.locked) {
22997             return;
22998         }
22999         this.selections.clear();
23000         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23001             this.selectRow(i, true);
23002         }
23003     },
23004
23005     /**
23006      * Returns True if there is a selection.
23007      * @return {Boolean}
23008      */
23009     hasSelection : function(){
23010         return this.selections.length > 0;
23011     },
23012
23013     /**
23014      * Returns True if the specified row is selected.
23015      * @param {Number/Record} record The record or index of the record to check
23016      * @return {Boolean}
23017      */
23018     isSelected : function(index){
23019             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23020         return (r && this.selections.key(r.id) ? true : false);
23021     },
23022
23023     /**
23024      * Returns True if the specified record id is selected.
23025      * @param {String} id The id of record to check
23026      * @return {Boolean}
23027      */
23028     isIdSelected : function(id){
23029         return (this.selections.key(id) ? true : false);
23030     },
23031
23032
23033     // private
23034     handleMouseDBClick : function(e, t){
23035         
23036     },
23037     // private
23038     handleMouseDown : function(e, t)
23039     {
23040             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23041         if(this.isLocked() || rowIndex < 0 ){
23042             return;
23043         };
23044         if(e.shiftKey && this.last !== false){
23045             var last = this.last;
23046             this.selectRange(last, rowIndex, e.ctrlKey);
23047             this.last = last; // reset the last
23048             t.focus();
23049     
23050         }else{
23051             var isSelected = this.isSelected(rowIndex);
23052             //Roo.log("select row:" + rowIndex);
23053             if(isSelected){
23054                 this.deselectRow(rowIndex);
23055             } else {
23056                         this.selectRow(rowIndex, true);
23057             }
23058     
23059             /*
23060                 if(e.button !== 0 && isSelected){
23061                 alert('rowIndex 2: ' + rowIndex);
23062                     view.focusRow(rowIndex);
23063                 }else if(e.ctrlKey && isSelected){
23064                     this.deselectRow(rowIndex);
23065                 }else if(!isSelected){
23066                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23067                     view.focusRow(rowIndex);
23068                 }
23069             */
23070         }
23071         this.fireEvent("afterselectionchange", this);
23072     },
23073     // private
23074     handleDragableRowClick :  function(grid, rowIndex, e) 
23075     {
23076         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23077             this.selectRow(rowIndex, false);
23078             grid.view.focusRow(rowIndex);
23079              this.fireEvent("afterselectionchange", this);
23080         }
23081     },
23082     
23083     /**
23084      * Selects multiple rows.
23085      * @param {Array} rows Array of the indexes of the row to select
23086      * @param {Boolean} keepExisting (optional) True to keep existing selections
23087      */
23088     selectRows : function(rows, keepExisting){
23089         if(!keepExisting){
23090             this.clearSelections();
23091         }
23092         for(var i = 0, len = rows.length; i < len; i++){
23093             this.selectRow(rows[i], true);
23094         }
23095     },
23096
23097     /**
23098      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23099      * @param {Number} startRow The index of the first row in the range
23100      * @param {Number} endRow The index of the last row in the range
23101      * @param {Boolean} keepExisting (optional) True to retain existing selections
23102      */
23103     selectRange : function(startRow, endRow, keepExisting){
23104         if(this.locked) {
23105             return;
23106         }
23107         if(!keepExisting){
23108             this.clearSelections();
23109         }
23110         if(startRow <= endRow){
23111             for(var i = startRow; i <= endRow; i++){
23112                 this.selectRow(i, true);
23113             }
23114         }else{
23115             for(var i = startRow; i >= endRow; i--){
23116                 this.selectRow(i, true);
23117             }
23118         }
23119     },
23120
23121     /**
23122      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23123      * @param {Number} startRow The index of the first row in the range
23124      * @param {Number} endRow The index of the last row in the range
23125      */
23126     deselectRange : function(startRow, endRow, preventViewNotify){
23127         if(this.locked) {
23128             return;
23129         }
23130         for(var i = startRow; i <= endRow; i++){
23131             this.deselectRow(i, preventViewNotify);
23132         }
23133     },
23134
23135     /**
23136      * Selects a row.
23137      * @param {Number} row The index of the row to select
23138      * @param {Boolean} keepExisting (optional) True to keep existing selections
23139      */
23140     selectRow : function(index, keepExisting, preventViewNotify)
23141     {
23142             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23143             return;
23144         }
23145         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23146             if(!keepExisting || this.singleSelect){
23147                 this.clearSelections();
23148             }
23149             
23150             var r = this.grid.store.getAt(index);
23151             //console.log('selectRow - record id :' + r.id);
23152             
23153             this.selections.add(r);
23154             this.last = this.lastActive = index;
23155             if(!preventViewNotify){
23156                 var proxy = new Roo.Element(
23157                                 this.grid.getRowDom(index)
23158                 );
23159                 proxy.addClass('bg-info info');
23160             }
23161             this.fireEvent("rowselect", this, index, r);
23162             this.fireEvent("selectionchange", this);
23163         }
23164     },
23165
23166     /**
23167      * Deselects a row.
23168      * @param {Number} row The index of the row to deselect
23169      */
23170     deselectRow : function(index, preventViewNotify)
23171     {
23172         if(this.locked) {
23173             return;
23174         }
23175         if(this.last == index){
23176             this.last = false;
23177         }
23178         if(this.lastActive == index){
23179             this.lastActive = false;
23180         }
23181         
23182         var r = this.grid.store.getAt(index);
23183         if (!r) {
23184             return;
23185         }
23186         
23187         this.selections.remove(r);
23188         //.console.log('deselectRow - record id :' + r.id);
23189         if(!preventViewNotify){
23190         
23191             var proxy = new Roo.Element(
23192                 this.grid.getRowDom(index)
23193             );
23194             proxy.removeClass('bg-info info');
23195         }
23196         this.fireEvent("rowdeselect", this, index);
23197         this.fireEvent("selectionchange", this);
23198     },
23199
23200     // private
23201     restoreLast : function(){
23202         if(this._last){
23203             this.last = this._last;
23204         }
23205     },
23206
23207     // private
23208     acceptsNav : function(row, col, cm){
23209         return !cm.isHidden(col) && cm.isCellEditable(col, row);
23210     },
23211
23212     // private
23213     onEditorKey : function(field, e){
23214         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23215         if(k == e.TAB){
23216             e.stopEvent();
23217             ed.completeEdit();
23218             if(e.shiftKey){
23219                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23220             }else{
23221                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23222             }
23223         }else if(k == e.ENTER && !e.ctrlKey){
23224             e.stopEvent();
23225             ed.completeEdit();
23226             if(e.shiftKey){
23227                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
23228             }else{
23229                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
23230             }
23231         }else if(k == e.ESC){
23232             ed.cancelEdit();
23233         }
23234         if(newCell){
23235             g.startEditing(newCell[0], newCell[1]);
23236         }
23237     }
23238 });
23239 /*
23240  * Based on:
23241  * Ext JS Library 1.1.1
23242  * Copyright(c) 2006-2007, Ext JS, LLC.
23243  *
23244  * Originally Released Under LGPL - original licence link has changed is not relivant.
23245  *
23246  * Fork - LGPL
23247  * <script type="text/javascript">
23248  */
23249  
23250 /**
23251  * @class Roo.bootstrap.PagingToolbar
23252  * @extends Roo.bootstrap.NavSimplebar
23253  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
23254  * @constructor
23255  * Create a new PagingToolbar
23256  * @param {Object} config The config object
23257  * @param {Roo.data.Store} store
23258  */
23259 Roo.bootstrap.PagingToolbar = function(config)
23260 {
23261     // old args format still supported... - xtype is prefered..
23262         // created from xtype...
23263     
23264     this.ds = config.dataSource;
23265     
23266     if (config.store && !this.ds) {
23267         this.store= Roo.factory(config.store, Roo.data);
23268         this.ds = this.store;
23269         this.ds.xmodule = this.xmodule || false;
23270     }
23271     
23272     this.toolbarItems = [];
23273     if (config.items) {
23274         this.toolbarItems = config.items;
23275     }
23276     
23277     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
23278     
23279     this.cursor = 0;
23280     
23281     if (this.ds) { 
23282         this.bind(this.ds);
23283     }
23284     
23285     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
23286     
23287 };
23288
23289 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
23290     /**
23291      * @cfg {Roo.data.Store} dataSource
23292      * The underlying data store providing the paged data
23293      */
23294     /**
23295      * @cfg {String/HTMLElement/Element} container
23296      * container The id or element that will contain the toolbar
23297      */
23298     /**
23299      * @cfg {Boolean} displayInfo
23300      * True to display the displayMsg (defaults to false)
23301      */
23302     /**
23303      * @cfg {Number} pageSize
23304      * The number of records to display per page (defaults to 20)
23305      */
23306     pageSize: 20,
23307     /**
23308      * @cfg {String} displayMsg
23309      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
23310      */
23311     displayMsg : 'Displaying {0} - {1} of {2}',
23312     /**
23313      * @cfg {String} emptyMsg
23314      * The message to display when no records are found (defaults to "No data to display")
23315      */
23316     emptyMsg : 'No data to display',
23317     /**
23318      * Customizable piece of the default paging text (defaults to "Page")
23319      * @type String
23320      */
23321     beforePageText : "Page",
23322     /**
23323      * Customizable piece of the default paging text (defaults to "of %0")
23324      * @type String
23325      */
23326     afterPageText : "of {0}",
23327     /**
23328      * Customizable piece of the default paging text (defaults to "First Page")
23329      * @type String
23330      */
23331     firstText : "First Page",
23332     /**
23333      * Customizable piece of the default paging text (defaults to "Previous Page")
23334      * @type String
23335      */
23336     prevText : "Previous Page",
23337     /**
23338      * Customizable piece of the default paging text (defaults to "Next Page")
23339      * @type String
23340      */
23341     nextText : "Next Page",
23342     /**
23343      * Customizable piece of the default paging text (defaults to "Last Page")
23344      * @type String
23345      */
23346     lastText : "Last Page",
23347     /**
23348      * Customizable piece of the default paging text (defaults to "Refresh")
23349      * @type String
23350      */
23351     refreshText : "Refresh",
23352
23353     buttons : false,
23354     // private
23355     onRender : function(ct, position) 
23356     {
23357         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
23358         this.navgroup.parentId = this.id;
23359         this.navgroup.onRender(this.el, null);
23360         // add the buttons to the navgroup
23361         
23362         if(this.displayInfo){
23363             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
23364             this.displayEl = this.el.select('.x-paging-info', true).first();
23365 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
23366 //            this.displayEl = navel.el.select('span',true).first();
23367         }
23368         
23369         var _this = this;
23370         
23371         if(this.buttons){
23372             Roo.each(_this.buttons, function(e){ // this might need to use render????
23373                Roo.factory(e).onRender(_this.el, null);
23374             });
23375         }
23376             
23377         Roo.each(_this.toolbarItems, function(e) {
23378             _this.navgroup.addItem(e);
23379         });
23380         
23381         
23382         this.first = this.navgroup.addItem({
23383             tooltip: this.firstText,
23384             cls: "prev",
23385             icon : 'fa fa-backward',
23386             disabled: true,
23387             preventDefault: true,
23388             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23389         });
23390         
23391         this.prev =  this.navgroup.addItem({
23392             tooltip: this.prevText,
23393             cls: "prev",
23394             icon : 'fa fa-step-backward',
23395             disabled: true,
23396             preventDefault: true,
23397             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
23398         });
23399     //this.addSeparator();
23400         
23401         
23402         var field = this.navgroup.addItem( {
23403             tagtype : 'span',
23404             cls : 'x-paging-position',
23405             
23406             html : this.beforePageText  +
23407                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23408                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
23409          } ); //?? escaped?
23410         
23411         this.field = field.el.select('input', true).first();
23412         this.field.on("keydown", this.onPagingKeydown, this);
23413         this.field.on("focus", function(){this.dom.select();});
23414     
23415     
23416         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
23417         //this.field.setHeight(18);
23418         //this.addSeparator();
23419         this.next = this.navgroup.addItem({
23420             tooltip: this.nextText,
23421             cls: "next",
23422             html : ' <i class="fa fa-step-forward">',
23423             disabled: true,
23424             preventDefault: true,
23425             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
23426         });
23427         this.last = this.navgroup.addItem({
23428             tooltip: this.lastText,
23429             icon : 'fa fa-forward',
23430             cls: "next",
23431             disabled: true,
23432             preventDefault: true,
23433             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
23434         });
23435     //this.addSeparator();
23436         this.loading = this.navgroup.addItem({
23437             tooltip: this.refreshText,
23438             icon: 'fa fa-refresh',
23439             preventDefault: true,
23440             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23441         });
23442         
23443     },
23444
23445     // private
23446     updateInfo : function(){
23447         if(this.displayEl){
23448             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23449             var msg = count == 0 ?
23450                 this.emptyMsg :
23451                 String.format(
23452                     this.displayMsg,
23453                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
23454                 );
23455             this.displayEl.update(msg);
23456         }
23457     },
23458
23459     // private
23460     onLoad : function(ds, r, o){
23461        this.cursor = o.params ? o.params.start : 0;
23462        var d = this.getPageData(),
23463             ap = d.activePage,
23464             ps = d.pages;
23465         
23466        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23467        this.field.dom.value = ap;
23468        this.first.setDisabled(ap == 1);
23469        this.prev.setDisabled(ap == 1);
23470        this.next.setDisabled(ap == ps);
23471        this.last.setDisabled(ap == ps);
23472        this.loading.enable();
23473        this.updateInfo();
23474     },
23475
23476     // private
23477     getPageData : function(){
23478         var total = this.ds.getTotalCount();
23479         return {
23480             total : total,
23481             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23482             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23483         };
23484     },
23485
23486     // private
23487     onLoadError : function(){
23488         this.loading.enable();
23489     },
23490
23491     // private
23492     onPagingKeydown : function(e){
23493         var k = e.getKey();
23494         var d = this.getPageData();
23495         if(k == e.RETURN){
23496             var v = this.field.dom.value, pageNum;
23497             if(!v || isNaN(pageNum = parseInt(v, 10))){
23498                 this.field.dom.value = d.activePage;
23499                 return;
23500             }
23501             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23502             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23503             e.stopEvent();
23504         }
23505         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))
23506         {
23507           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23508           this.field.dom.value = pageNum;
23509           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23510           e.stopEvent();
23511         }
23512         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23513         {
23514           var v = this.field.dom.value, pageNum; 
23515           var increment = (e.shiftKey) ? 10 : 1;
23516           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23517                 increment *= -1;
23518           }
23519           if(!v || isNaN(pageNum = parseInt(v, 10))) {
23520             this.field.dom.value = d.activePage;
23521             return;
23522           }
23523           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23524           {
23525             this.field.dom.value = parseInt(v, 10) + increment;
23526             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23527             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23528           }
23529           e.stopEvent();
23530         }
23531     },
23532
23533     // private
23534     beforeLoad : function(){
23535         if(this.loading){
23536             this.loading.disable();
23537         }
23538     },
23539
23540     // private
23541     onClick : function(which){
23542         
23543         var ds = this.ds;
23544         if (!ds) {
23545             return;
23546         }
23547         
23548         switch(which){
23549             case "first":
23550                 ds.load({params:{start: 0, limit: this.pageSize}});
23551             break;
23552             case "prev":
23553                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23554             break;
23555             case "next":
23556                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23557             break;
23558             case "last":
23559                 var total = ds.getTotalCount();
23560                 var extra = total % this.pageSize;
23561                 var lastStart = extra ? (total - extra) : total-this.pageSize;
23562                 ds.load({params:{start: lastStart, limit: this.pageSize}});
23563             break;
23564             case "refresh":
23565                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23566             break;
23567         }
23568     },
23569
23570     /**
23571      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23572      * @param {Roo.data.Store} store The data store to unbind
23573      */
23574     unbind : function(ds){
23575         ds.un("beforeload", this.beforeLoad, this);
23576         ds.un("load", this.onLoad, this);
23577         ds.un("loadexception", this.onLoadError, this);
23578         ds.un("remove", this.updateInfo, this);
23579         ds.un("add", this.updateInfo, this);
23580         this.ds = undefined;
23581     },
23582
23583     /**
23584      * Binds the paging toolbar to the specified {@link Roo.data.Store}
23585      * @param {Roo.data.Store} store The data store to bind
23586      */
23587     bind : function(ds){
23588         ds.on("beforeload", this.beforeLoad, this);
23589         ds.on("load", this.onLoad, this);
23590         ds.on("loadexception", this.onLoadError, this);
23591         ds.on("remove", this.updateInfo, this);
23592         ds.on("add", this.updateInfo, this);
23593         this.ds = ds;
23594     }
23595 });/*
23596  * - LGPL
23597  *
23598  * element
23599  * 
23600  */
23601
23602 /**
23603  * @class Roo.bootstrap.MessageBar
23604  * @extends Roo.bootstrap.Component
23605  * Bootstrap MessageBar class
23606  * @cfg {String} html contents of the MessageBar
23607  * @cfg {String} weight (info | success | warning | danger) default info
23608  * @cfg {String} beforeClass insert the bar before the given class
23609  * @cfg {Boolean} closable (true | false) default false
23610  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23611  * 
23612  * @constructor
23613  * Create a new Element
23614  * @param {Object} config The config object
23615  */
23616
23617 Roo.bootstrap.MessageBar = function(config){
23618     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23619 };
23620
23621 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
23622     
23623     html: '',
23624     weight: 'info',
23625     closable: false,
23626     fixed: false,
23627     beforeClass: 'bootstrap-sticky-wrap',
23628     
23629     getAutoCreate : function(){
23630         
23631         var cfg = {
23632             tag: 'div',
23633             cls: 'alert alert-dismissable alert-' + this.weight,
23634             cn: [
23635                 {
23636                     tag: 'span',
23637                     cls: 'message',
23638                     html: this.html || ''
23639                 }
23640             ]
23641         };
23642         
23643         if(this.fixed){
23644             cfg.cls += ' alert-messages-fixed';
23645         }
23646         
23647         if(this.closable){
23648             cfg.cn.push({
23649                 tag: 'button',
23650                 cls: 'close',
23651                 html: 'x'
23652             });
23653         }
23654         
23655         return cfg;
23656     },
23657     
23658     onRender : function(ct, position)
23659     {
23660         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23661         
23662         if(!this.el){
23663             var cfg = Roo.apply({},  this.getAutoCreate());
23664             cfg.id = Roo.id();
23665             
23666             if (this.cls) {
23667                 cfg.cls += ' ' + this.cls;
23668             }
23669             if (this.style) {
23670                 cfg.style = this.style;
23671             }
23672             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23673             
23674             this.el.setVisibilityMode(Roo.Element.DISPLAY);
23675         }
23676         
23677         this.el.select('>button.close').on('click', this.hide, this);
23678         
23679     },
23680     
23681     show : function()
23682     {
23683         if (!this.rendered) {
23684             this.render();
23685         }
23686         
23687         this.el.show();
23688         
23689         this.fireEvent('show', this);
23690         
23691     },
23692     
23693     hide : function()
23694     {
23695         if (!this.rendered) {
23696             this.render();
23697         }
23698         
23699         this.el.hide();
23700         
23701         this.fireEvent('hide', this);
23702     },
23703     
23704     update : function()
23705     {
23706 //        var e = this.el.dom.firstChild;
23707 //        
23708 //        if(this.closable){
23709 //            e = e.nextSibling;
23710 //        }
23711 //        
23712 //        e.data = this.html || '';
23713
23714         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23715     }
23716    
23717 });
23718
23719  
23720
23721      /*
23722  * - LGPL
23723  *
23724  * Graph
23725  * 
23726  */
23727
23728
23729 /**
23730  * @class Roo.bootstrap.Graph
23731  * @extends Roo.bootstrap.Component
23732  * Bootstrap Graph class
23733 > Prameters
23734  -sm {number} sm 4
23735  -md {number} md 5
23736  @cfg {String} graphtype  bar | vbar | pie
23737  @cfg {number} g_x coodinator | centre x (pie)
23738  @cfg {number} g_y coodinator | centre y (pie)
23739  @cfg {number} g_r radius (pie)
23740  @cfg {number} g_height height of the chart (respected by all elements in the set)
23741  @cfg {number} g_width width of the chart (respected by all elements in the set)
23742  @cfg {Object} title The title of the chart
23743     
23744  -{Array}  values
23745  -opts (object) options for the chart 
23746      o {
23747      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23748      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23749      o vgutter (number)
23750      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.
23751      o stacked (boolean) whether or not to tread values as in a stacked bar chart
23752      o to
23753      o stretch (boolean)
23754      o }
23755  -opts (object) options for the pie
23756      o{
23757      o cut
23758      o startAngle (number)
23759      o endAngle (number)
23760      } 
23761  *
23762  * @constructor
23763  * Create a new Input
23764  * @param {Object} config The config object
23765  */
23766
23767 Roo.bootstrap.Graph = function(config){
23768     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23769     
23770     this.addEvents({
23771         // img events
23772         /**
23773          * @event click
23774          * The img click event for the img.
23775          * @param {Roo.EventObject} e
23776          */
23777         "click" : true
23778     });
23779 };
23780
23781 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
23782     
23783     sm: 4,
23784     md: 5,
23785     graphtype: 'bar',
23786     g_height: 250,
23787     g_width: 400,
23788     g_x: 50,
23789     g_y: 50,
23790     g_r: 30,
23791     opts:{
23792         //g_colors: this.colors,
23793         g_type: 'soft',
23794         g_gutter: '20%'
23795
23796     },
23797     title : false,
23798
23799     getAutoCreate : function(){
23800         
23801         var cfg = {
23802             tag: 'div',
23803             html : null
23804         };
23805         
23806         
23807         return  cfg;
23808     },
23809
23810     onRender : function(ct,position){
23811         
23812         
23813         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23814         
23815         if (typeof(Raphael) == 'undefined') {
23816             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23817             return;
23818         }
23819         
23820         this.raphael = Raphael(this.el.dom);
23821         
23822                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23823                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23824                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23825                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23826                 /*
23827                 r.text(160, 10, "Single Series Chart").attr(txtattr);
23828                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23829                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23830                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23831                 
23832                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23833                 r.barchart(330, 10, 300, 220, data1);
23834                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23835                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23836                 */
23837                 
23838                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23839                 // r.barchart(30, 30, 560, 250,  xdata, {
23840                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23841                 //     axis : "0 0 1 1",
23842                 //     axisxlabels :  xdata
23843                 //     //yvalues : cols,
23844                    
23845                 // });
23846 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23847 //        
23848 //        this.load(null,xdata,{
23849 //                axis : "0 0 1 1",
23850 //                axisxlabels :  xdata
23851 //                });
23852
23853     },
23854
23855     load : function(graphtype,xdata,opts)
23856     {
23857         this.raphael.clear();
23858         if(!graphtype) {
23859             graphtype = this.graphtype;
23860         }
23861         if(!opts){
23862             opts = this.opts;
23863         }
23864         var r = this.raphael,
23865             fin = function () {
23866                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23867             },
23868             fout = function () {
23869                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23870             },
23871             pfin = function() {
23872                 this.sector.stop();
23873                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23874
23875                 if (this.label) {
23876                     this.label[0].stop();
23877                     this.label[0].attr({ r: 7.5 });
23878                     this.label[1].attr({ "font-weight": 800 });
23879                 }
23880             },
23881             pfout = function() {
23882                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23883
23884                 if (this.label) {
23885                     this.label[0].animate({ r: 5 }, 500, "bounce");
23886                     this.label[1].attr({ "font-weight": 400 });
23887                 }
23888             };
23889
23890         switch(graphtype){
23891             case 'bar':
23892                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23893                 break;
23894             case 'hbar':
23895                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23896                 break;
23897             case 'pie':
23898 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
23899 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23900 //            
23901                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23902                 
23903                 break;
23904
23905         }
23906         
23907         if(this.title){
23908             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23909         }
23910         
23911     },
23912     
23913     setTitle: function(o)
23914     {
23915         this.title = o;
23916     },
23917     
23918     initEvents: function() {
23919         
23920         if(!this.href){
23921             this.el.on('click', this.onClick, this);
23922         }
23923     },
23924     
23925     onClick : function(e)
23926     {
23927         Roo.log('img onclick');
23928         this.fireEvent('click', this, e);
23929     }
23930    
23931 });
23932
23933  
23934 /*
23935  * - LGPL
23936  *
23937  * numberBox
23938  * 
23939  */
23940 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23941
23942 /**
23943  * @class Roo.bootstrap.dash.NumberBox
23944  * @extends Roo.bootstrap.Component
23945  * Bootstrap NumberBox class
23946  * @cfg {String} headline Box headline
23947  * @cfg {String} content Box content
23948  * @cfg {String} icon Box icon
23949  * @cfg {String} footer Footer text
23950  * @cfg {String} fhref Footer href
23951  * 
23952  * @constructor
23953  * Create a new NumberBox
23954  * @param {Object} config The config object
23955  */
23956
23957
23958 Roo.bootstrap.dash.NumberBox = function(config){
23959     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23960     
23961 };
23962
23963 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
23964     
23965     headline : '',
23966     content : '',
23967     icon : '',
23968     footer : '',
23969     fhref : '',
23970     ficon : '',
23971     
23972     getAutoCreate : function(){
23973         
23974         var cfg = {
23975             tag : 'div',
23976             cls : 'small-box ',
23977             cn : [
23978                 {
23979                     tag : 'div',
23980                     cls : 'inner',
23981                     cn :[
23982                         {
23983                             tag : 'h3',
23984                             cls : 'roo-headline',
23985                             html : this.headline
23986                         },
23987                         {
23988                             tag : 'p',
23989                             cls : 'roo-content',
23990                             html : this.content
23991                         }
23992                     ]
23993                 }
23994             ]
23995         };
23996         
23997         if(this.icon){
23998             cfg.cn.push({
23999                 tag : 'div',
24000                 cls : 'icon',
24001                 cn :[
24002                     {
24003                         tag : 'i',
24004                         cls : 'ion ' + this.icon
24005                     }
24006                 ]
24007             });
24008         }
24009         
24010         if(this.footer){
24011             var footer = {
24012                 tag : 'a',
24013                 cls : 'small-box-footer',
24014                 href : this.fhref || '#',
24015                 html : this.footer
24016             };
24017             
24018             cfg.cn.push(footer);
24019             
24020         }
24021         
24022         return  cfg;
24023     },
24024
24025     onRender : function(ct,position){
24026         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24027
24028
24029        
24030                 
24031     },
24032
24033     setHeadline: function (value)
24034     {
24035         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24036     },
24037     
24038     setFooter: function (value, href)
24039     {
24040         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24041         
24042         if(href){
24043             this.el.select('a.small-box-footer',true).first().attr('href', href);
24044         }
24045         
24046     },
24047
24048     setContent: function (value)
24049     {
24050         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24051     },
24052
24053     initEvents: function() 
24054     {   
24055         
24056     }
24057     
24058 });
24059
24060  
24061 /*
24062  * - LGPL
24063  *
24064  * TabBox
24065  * 
24066  */
24067 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24068
24069 /**
24070  * @class Roo.bootstrap.dash.TabBox
24071  * @extends Roo.bootstrap.Component
24072  * Bootstrap TabBox class
24073  * @cfg {String} title Title of the TabBox
24074  * @cfg {String} icon Icon of the TabBox
24075  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24076  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24077  * 
24078  * @constructor
24079  * Create a new TabBox
24080  * @param {Object} config The config object
24081  */
24082
24083
24084 Roo.bootstrap.dash.TabBox = function(config){
24085     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24086     this.addEvents({
24087         // raw events
24088         /**
24089          * @event addpane
24090          * When a pane is added
24091          * @param {Roo.bootstrap.dash.TabPane} pane
24092          */
24093         "addpane" : true,
24094         /**
24095          * @event activatepane
24096          * When a pane is activated
24097          * @param {Roo.bootstrap.dash.TabPane} pane
24098          */
24099         "activatepane" : true
24100         
24101          
24102     });
24103     
24104     this.panes = [];
24105 };
24106
24107 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24108
24109     title : '',
24110     icon : false,
24111     showtabs : true,
24112     tabScrollable : false,
24113     
24114     getChildContainer : function()
24115     {
24116         return this.el.select('.tab-content', true).first();
24117     },
24118     
24119     getAutoCreate : function(){
24120         
24121         var header = {
24122             tag: 'li',
24123             cls: 'pull-left header',
24124             html: this.title,
24125             cn : []
24126         };
24127         
24128         if(this.icon){
24129             header.cn.push({
24130                 tag: 'i',
24131                 cls: 'fa ' + this.icon
24132             });
24133         }
24134         
24135         var h = {
24136             tag: 'ul',
24137             cls: 'nav nav-tabs pull-right',
24138             cn: [
24139                 header
24140             ]
24141         };
24142         
24143         if(this.tabScrollable){
24144             h = {
24145                 tag: 'div',
24146                 cls: 'tab-header',
24147                 cn: [
24148                     {
24149                         tag: 'ul',
24150                         cls: 'nav nav-tabs pull-right',
24151                         cn: [
24152                             header
24153                         ]
24154                     }
24155                 ]
24156             };
24157         }
24158         
24159         var cfg = {
24160             tag: 'div',
24161             cls: 'nav-tabs-custom',
24162             cn: [
24163                 h,
24164                 {
24165                     tag: 'div',
24166                     cls: 'tab-content no-padding',
24167                     cn: []
24168                 }
24169             ]
24170         };
24171
24172         return  cfg;
24173     },
24174     initEvents : function()
24175     {
24176         //Roo.log('add add pane handler');
24177         this.on('addpane', this.onAddPane, this);
24178     },
24179      /**
24180      * Updates the box title
24181      * @param {String} html to set the title to.
24182      */
24183     setTitle : function(value)
24184     {
24185         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24186     },
24187     onAddPane : function(pane)
24188     {
24189         this.panes.push(pane);
24190         //Roo.log('addpane');
24191         //Roo.log(pane);
24192         // tabs are rendere left to right..
24193         if(!this.showtabs){
24194             return;
24195         }
24196         
24197         var ctr = this.el.select('.nav-tabs', true).first();
24198          
24199          
24200         var existing = ctr.select('.nav-tab',true);
24201         var qty = existing.getCount();;
24202         
24203         
24204         var tab = ctr.createChild({
24205             tag : 'li',
24206             cls : 'nav-tab' + (qty ? '' : ' active'),
24207             cn : [
24208                 {
24209                     tag : 'a',
24210                     href:'#',
24211                     html : pane.title
24212                 }
24213             ]
24214         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24215         pane.tab = tab;
24216         
24217         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24218         if (!qty) {
24219             pane.el.addClass('active');
24220         }
24221         
24222                 
24223     },
24224     onTabClick : function(ev,un,ob,pane)
24225     {
24226         //Roo.log('tab - prev default');
24227         ev.preventDefault();
24228         
24229         
24230         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
24231         pane.tab.addClass('active');
24232         //Roo.log(pane.title);
24233         this.getChildContainer().select('.tab-pane',true).removeClass('active');
24234         // technically we should have a deactivate event.. but maybe add later.
24235         // and it should not de-activate the selected tab...
24236         this.fireEvent('activatepane', pane);
24237         pane.el.addClass('active');
24238         pane.fireEvent('activate');
24239         
24240         
24241     },
24242     
24243     getActivePane : function()
24244     {
24245         var r = false;
24246         Roo.each(this.panes, function(p) {
24247             if(p.el.hasClass('active')){
24248                 r = p;
24249                 return false;
24250             }
24251             
24252             return;
24253         });
24254         
24255         return r;
24256     }
24257     
24258     
24259 });
24260
24261  
24262 /*
24263  * - LGPL
24264  *
24265  * Tab pane
24266  * 
24267  */
24268 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24269 /**
24270  * @class Roo.bootstrap.TabPane
24271  * @extends Roo.bootstrap.Component
24272  * Bootstrap TabPane class
24273  * @cfg {Boolean} active (false | true) Default false
24274  * @cfg {String} title title of panel
24275
24276  * 
24277  * @constructor
24278  * Create a new TabPane
24279  * @param {Object} config The config object
24280  */
24281
24282 Roo.bootstrap.dash.TabPane = function(config){
24283     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
24284     
24285     this.addEvents({
24286         // raw events
24287         /**
24288          * @event activate
24289          * When a pane is activated
24290          * @param {Roo.bootstrap.dash.TabPane} pane
24291          */
24292         "activate" : true
24293          
24294     });
24295 };
24296
24297 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
24298     
24299     active : false,
24300     title : '',
24301     
24302     // the tabBox that this is attached to.
24303     tab : false,
24304      
24305     getAutoCreate : function() 
24306     {
24307         var cfg = {
24308             tag: 'div',
24309             cls: 'tab-pane'
24310         };
24311         
24312         if(this.active){
24313             cfg.cls += ' active';
24314         }
24315         
24316         return cfg;
24317     },
24318     initEvents  : function()
24319     {
24320         //Roo.log('trigger add pane handler');
24321         this.parent().fireEvent('addpane', this)
24322     },
24323     
24324      /**
24325      * Updates the tab title 
24326      * @param {String} html to set the title to.
24327      */
24328     setTitle: function(str)
24329     {
24330         if (!this.tab) {
24331             return;
24332         }
24333         this.title = str;
24334         this.tab.select('a', true).first().dom.innerHTML = str;
24335         
24336     }
24337     
24338     
24339     
24340 });
24341
24342  
24343
24344
24345  /*
24346  * - LGPL
24347  *
24348  * menu
24349  * 
24350  */
24351 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24352
24353 /**
24354  * @class Roo.bootstrap.menu.Menu
24355  * @extends Roo.bootstrap.Component
24356  * Bootstrap Menu class - container for Menu
24357  * @cfg {String} html Text of the menu
24358  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
24359  * @cfg {String} icon Font awesome icon
24360  * @cfg {String} pos Menu align to (top | bottom) default bottom
24361  * 
24362  * 
24363  * @constructor
24364  * Create a new Menu
24365  * @param {Object} config The config object
24366  */
24367
24368
24369 Roo.bootstrap.menu.Menu = function(config){
24370     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
24371     
24372     this.addEvents({
24373         /**
24374          * @event beforeshow
24375          * Fires before this menu is displayed
24376          * @param {Roo.bootstrap.menu.Menu} this
24377          */
24378         beforeshow : true,
24379         /**
24380          * @event beforehide
24381          * Fires before this menu is hidden
24382          * @param {Roo.bootstrap.menu.Menu} this
24383          */
24384         beforehide : true,
24385         /**
24386          * @event show
24387          * Fires after this menu is displayed
24388          * @param {Roo.bootstrap.menu.Menu} this
24389          */
24390         show : true,
24391         /**
24392          * @event hide
24393          * Fires after this menu is hidden
24394          * @param {Roo.bootstrap.menu.Menu} this
24395          */
24396         hide : true,
24397         /**
24398          * @event click
24399          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24400          * @param {Roo.bootstrap.menu.Menu} this
24401          * @param {Roo.EventObject} e
24402          */
24403         click : true
24404     });
24405     
24406 };
24407
24408 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
24409     
24410     submenu : false,
24411     html : '',
24412     weight : 'default',
24413     icon : false,
24414     pos : 'bottom',
24415     
24416     
24417     getChildContainer : function() {
24418         if(this.isSubMenu){
24419             return this.el;
24420         }
24421         
24422         return this.el.select('ul.dropdown-menu', true).first();  
24423     },
24424     
24425     getAutoCreate : function()
24426     {
24427         var text = [
24428             {
24429                 tag : 'span',
24430                 cls : 'roo-menu-text',
24431                 html : this.html
24432             }
24433         ];
24434         
24435         if(this.icon){
24436             text.unshift({
24437                 tag : 'i',
24438                 cls : 'fa ' + this.icon
24439             })
24440         }
24441         
24442         
24443         var cfg = {
24444             tag : 'div',
24445             cls : 'btn-group',
24446             cn : [
24447                 {
24448                     tag : 'button',
24449                     cls : 'dropdown-button btn btn-' + this.weight,
24450                     cn : text
24451                 },
24452                 {
24453                     tag : 'button',
24454                     cls : 'dropdown-toggle btn btn-' + this.weight,
24455                     cn : [
24456                         {
24457                             tag : 'span',
24458                             cls : 'caret'
24459                         }
24460                     ]
24461                 },
24462                 {
24463                     tag : 'ul',
24464                     cls : 'dropdown-menu'
24465                 }
24466             ]
24467             
24468         };
24469         
24470         if(this.pos == 'top'){
24471             cfg.cls += ' dropup';
24472         }
24473         
24474         if(this.isSubMenu){
24475             cfg = {
24476                 tag : 'ul',
24477                 cls : 'dropdown-menu'
24478             }
24479         }
24480         
24481         return cfg;
24482     },
24483     
24484     onRender : function(ct, position)
24485     {
24486         this.isSubMenu = ct.hasClass('dropdown-submenu');
24487         
24488         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24489     },
24490     
24491     initEvents : function() 
24492     {
24493         if(this.isSubMenu){
24494             return;
24495         }
24496         
24497         this.hidden = true;
24498         
24499         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24500         this.triggerEl.on('click', this.onTriggerPress, this);
24501         
24502         this.buttonEl = this.el.select('button.dropdown-button', true).first();
24503         this.buttonEl.on('click', this.onClick, this);
24504         
24505     },
24506     
24507     list : function()
24508     {
24509         if(this.isSubMenu){
24510             return this.el;
24511         }
24512         
24513         return this.el.select('ul.dropdown-menu', true).first();
24514     },
24515     
24516     onClick : function(e)
24517     {
24518         this.fireEvent("click", this, e);
24519     },
24520     
24521     onTriggerPress  : function(e)
24522     {   
24523         if (this.isVisible()) {
24524             this.hide();
24525         } else {
24526             this.show();
24527         }
24528     },
24529     
24530     isVisible : function(){
24531         return !this.hidden;
24532     },
24533     
24534     show : function()
24535     {
24536         this.fireEvent("beforeshow", this);
24537         
24538         this.hidden = false;
24539         this.el.addClass('open');
24540         
24541         Roo.get(document).on("mouseup", this.onMouseUp, this);
24542         
24543         this.fireEvent("show", this);
24544         
24545         
24546     },
24547     
24548     hide : function()
24549     {
24550         this.fireEvent("beforehide", this);
24551         
24552         this.hidden = true;
24553         this.el.removeClass('open');
24554         
24555         Roo.get(document).un("mouseup", this.onMouseUp);
24556         
24557         this.fireEvent("hide", this);
24558     },
24559     
24560     onMouseUp : function()
24561     {
24562         this.hide();
24563     }
24564     
24565 });
24566
24567  
24568  /*
24569  * - LGPL
24570  *
24571  * menu item
24572  * 
24573  */
24574 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24575
24576 /**
24577  * @class Roo.bootstrap.menu.Item
24578  * @extends Roo.bootstrap.Component
24579  * Bootstrap MenuItem class
24580  * @cfg {Boolean} submenu (true | false) default false
24581  * @cfg {String} html text of the item
24582  * @cfg {String} href the link
24583  * @cfg {Boolean} disable (true | false) default false
24584  * @cfg {Boolean} preventDefault (true | false) default true
24585  * @cfg {String} icon Font awesome icon
24586  * @cfg {String} pos Submenu align to (left | right) default right 
24587  * 
24588  * 
24589  * @constructor
24590  * Create a new Item
24591  * @param {Object} config The config object
24592  */
24593
24594
24595 Roo.bootstrap.menu.Item = function(config){
24596     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24597     this.addEvents({
24598         /**
24599          * @event mouseover
24600          * Fires when the mouse is hovering over this menu
24601          * @param {Roo.bootstrap.menu.Item} this
24602          * @param {Roo.EventObject} e
24603          */
24604         mouseover : true,
24605         /**
24606          * @event mouseout
24607          * Fires when the mouse exits this menu
24608          * @param {Roo.bootstrap.menu.Item} this
24609          * @param {Roo.EventObject} e
24610          */
24611         mouseout : true,
24612         // raw events
24613         /**
24614          * @event click
24615          * The raw click event for the entire grid.
24616          * @param {Roo.EventObject} e
24617          */
24618         click : true
24619     });
24620 };
24621
24622 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
24623     
24624     submenu : false,
24625     href : '',
24626     html : '',
24627     preventDefault: true,
24628     disable : false,
24629     icon : false,
24630     pos : 'right',
24631     
24632     getAutoCreate : function()
24633     {
24634         var text = [
24635             {
24636                 tag : 'span',
24637                 cls : 'roo-menu-item-text',
24638                 html : this.html
24639             }
24640         ];
24641         
24642         if(this.icon){
24643             text.unshift({
24644                 tag : 'i',
24645                 cls : 'fa ' + this.icon
24646             })
24647         }
24648         
24649         var cfg = {
24650             tag : 'li',
24651             cn : [
24652                 {
24653                     tag : 'a',
24654                     href : this.href || '#',
24655                     cn : text
24656                 }
24657             ]
24658         };
24659         
24660         if(this.disable){
24661             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24662         }
24663         
24664         if(this.submenu){
24665             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24666             
24667             if(this.pos == 'left'){
24668                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24669             }
24670         }
24671         
24672         return cfg;
24673     },
24674     
24675     initEvents : function() 
24676     {
24677         this.el.on('mouseover', this.onMouseOver, this);
24678         this.el.on('mouseout', this.onMouseOut, this);
24679         
24680         this.el.select('a', true).first().on('click', this.onClick, this);
24681         
24682     },
24683     
24684     onClick : function(e)
24685     {
24686         if(this.preventDefault){
24687             e.preventDefault();
24688         }
24689         
24690         this.fireEvent("click", this, e);
24691     },
24692     
24693     onMouseOver : function(e)
24694     {
24695         if(this.submenu && this.pos == 'left'){
24696             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24697         }
24698         
24699         this.fireEvent("mouseover", this, e);
24700     },
24701     
24702     onMouseOut : function(e)
24703     {
24704         this.fireEvent("mouseout", this, e);
24705     }
24706 });
24707
24708  
24709
24710  /*
24711  * - LGPL
24712  *
24713  * menu separator
24714  * 
24715  */
24716 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24717
24718 /**
24719  * @class Roo.bootstrap.menu.Separator
24720  * @extends Roo.bootstrap.Component
24721  * Bootstrap Separator class
24722  * 
24723  * @constructor
24724  * Create a new Separator
24725  * @param {Object} config The config object
24726  */
24727
24728
24729 Roo.bootstrap.menu.Separator = function(config){
24730     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24731 };
24732
24733 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
24734     
24735     getAutoCreate : function(){
24736         var cfg = {
24737             tag : 'li',
24738             cls: 'divider'
24739         };
24740         
24741         return cfg;
24742     }
24743    
24744 });
24745
24746  
24747
24748  /*
24749  * - LGPL
24750  *
24751  * Tooltip
24752  * 
24753  */
24754
24755 /**
24756  * @class Roo.bootstrap.Tooltip
24757  * Bootstrap Tooltip class
24758  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24759  * to determine which dom element triggers the tooltip.
24760  * 
24761  * It needs to add support for additional attributes like tooltip-position
24762  * 
24763  * @constructor
24764  * Create a new Toolti
24765  * @param {Object} config The config object
24766  */
24767
24768 Roo.bootstrap.Tooltip = function(config){
24769     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24770 };
24771
24772 Roo.apply(Roo.bootstrap.Tooltip, {
24773     /**
24774      * @function init initialize tooltip monitoring.
24775      * @static
24776      */
24777     currentEl : false,
24778     currentTip : false,
24779     currentRegion : false,
24780     
24781     //  init : delay?
24782     
24783     init : function()
24784     {
24785         Roo.get(document).on('mouseover', this.enter ,this);
24786         Roo.get(document).on('mouseout', this.leave, this);
24787          
24788         
24789         this.currentTip = new Roo.bootstrap.Tooltip();
24790     },
24791     
24792     enter : function(ev)
24793     {
24794         var dom = ev.getTarget();
24795         
24796         //Roo.log(['enter',dom]);
24797         var el = Roo.fly(dom);
24798         if (this.currentEl) {
24799             //Roo.log(dom);
24800             //Roo.log(this.currentEl);
24801             //Roo.log(this.currentEl.contains(dom));
24802             if (this.currentEl == el) {
24803                 return;
24804             }
24805             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24806                 return;
24807             }
24808
24809         }
24810         
24811         if (this.currentTip.el) {
24812             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24813         }    
24814         //Roo.log(ev);
24815         
24816         if(!el || el.dom == document){
24817             return;
24818         }
24819         
24820         var bindEl = el;
24821         
24822         // you can not look for children, as if el is the body.. then everythign is the child..
24823         if (!el.attr('tooltip')) { //
24824             if (!el.select("[tooltip]").elements.length) {
24825                 return;
24826             }
24827             // is the mouse over this child...?
24828             bindEl = el.select("[tooltip]").first();
24829             var xy = ev.getXY();
24830             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24831                 //Roo.log("not in region.");
24832                 return;
24833             }
24834             //Roo.log("child element over..");
24835             
24836         }
24837         this.currentEl = bindEl;
24838         this.currentTip.bind(bindEl);
24839         this.currentRegion = Roo.lib.Region.getRegion(dom);
24840         this.currentTip.enter();
24841         
24842     },
24843     leave : function(ev)
24844     {
24845         var dom = ev.getTarget();
24846         //Roo.log(['leave',dom]);
24847         if (!this.currentEl) {
24848             return;
24849         }
24850         
24851         
24852         if (dom != this.currentEl.dom) {
24853             return;
24854         }
24855         var xy = ev.getXY();
24856         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
24857             return;
24858         }
24859         // only activate leave if mouse cursor is outside... bounding box..
24860         
24861         
24862         
24863         
24864         if (this.currentTip) {
24865             this.currentTip.leave();
24866         }
24867         //Roo.log('clear currentEl');
24868         this.currentEl = false;
24869         
24870         
24871     },
24872     alignment : {
24873         'left' : ['r-l', [-2,0], 'right'],
24874         'right' : ['l-r', [2,0], 'left'],
24875         'bottom' : ['t-b', [0,2], 'top'],
24876         'top' : [ 'b-t', [0,-2], 'bottom']
24877     }
24878     
24879 });
24880
24881
24882 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
24883     
24884     
24885     bindEl : false,
24886     
24887     delay : null, // can be { show : 300 , hide: 500}
24888     
24889     timeout : null,
24890     
24891     hoverState : null, //???
24892     
24893     placement : 'bottom', 
24894     
24895     getAutoCreate : function(){
24896     
24897         var cfg = {
24898            cls : 'tooltip',
24899            role : 'tooltip',
24900            cn : [
24901                 {
24902                     cls : 'tooltip-arrow'
24903                 },
24904                 {
24905                     cls : 'tooltip-inner'
24906                 }
24907            ]
24908         };
24909         
24910         return cfg;
24911     },
24912     bind : function(el)
24913     {
24914         this.bindEl = el;
24915     },
24916       
24917     
24918     enter : function () {
24919        
24920         if (this.timeout != null) {
24921             clearTimeout(this.timeout);
24922         }
24923         
24924         this.hoverState = 'in';
24925          //Roo.log("enter - show");
24926         if (!this.delay || !this.delay.show) {
24927             this.show();
24928             return;
24929         }
24930         var _t = this;
24931         this.timeout = setTimeout(function () {
24932             if (_t.hoverState == 'in') {
24933                 _t.show();
24934             }
24935         }, this.delay.show);
24936     },
24937     leave : function()
24938     {
24939         clearTimeout(this.timeout);
24940     
24941         this.hoverState = 'out';
24942          if (!this.delay || !this.delay.hide) {
24943             this.hide();
24944             return;
24945         }
24946        
24947         var _t = this;
24948         this.timeout = setTimeout(function () {
24949             //Roo.log("leave - timeout");
24950             
24951             if (_t.hoverState == 'out') {
24952                 _t.hide();
24953                 Roo.bootstrap.Tooltip.currentEl = false;
24954             }
24955         }, delay);
24956     },
24957     
24958     show : function ()
24959     {
24960         if (!this.el) {
24961             this.render(document.body);
24962         }
24963         // set content.
24964         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24965         
24966         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24967         
24968         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24969         
24970         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24971         
24972         var placement = typeof this.placement == 'function' ?
24973             this.placement.call(this, this.el, on_el) :
24974             this.placement;
24975             
24976         var autoToken = /\s?auto?\s?/i;
24977         var autoPlace = autoToken.test(placement);
24978         if (autoPlace) {
24979             placement = placement.replace(autoToken, '') || 'top';
24980         }
24981         
24982         //this.el.detach()
24983         //this.el.setXY([0,0]);
24984         this.el.show();
24985         //this.el.dom.style.display='block';
24986         
24987         //this.el.appendTo(on_el);
24988         
24989         var p = this.getPosition();
24990         var box = this.el.getBox();
24991         
24992         if (autoPlace) {
24993             // fixme..
24994         }
24995         
24996         var align = Roo.bootstrap.Tooltip.alignment[placement];
24997         
24998         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24999         
25000         if(placement == 'top' || placement == 'bottom'){
25001             if(xy[0] < 0){
25002                 placement = 'right';
25003             }
25004             
25005             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25006                 placement = 'left';
25007             }
25008             
25009             var scroll = Roo.select('body', true).first().getScroll();
25010             
25011             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25012                 placement = 'top';
25013             }
25014             
25015         }
25016         
25017         align = Roo.bootstrap.Tooltip.alignment[placement];
25018         
25019         this.el.alignTo(this.bindEl, align[0],align[1]);
25020         //var arrow = this.el.select('.arrow',true).first();
25021         //arrow.set(align[2], 
25022         
25023         this.el.addClass(placement);
25024         
25025         this.el.addClass('in fade');
25026         
25027         this.hoverState = null;
25028         
25029         if (this.el.hasClass('fade')) {
25030             // fade it?
25031         }
25032         
25033     },
25034     hide : function()
25035     {
25036          
25037         if (!this.el) {
25038             return;
25039         }
25040         //this.el.setXY([0,0]);
25041         this.el.removeClass('in');
25042         //this.el.hide();
25043         
25044     }
25045     
25046 });
25047  
25048
25049  /*
25050  * - LGPL
25051  *
25052  * Location Picker
25053  * 
25054  */
25055
25056 /**
25057  * @class Roo.bootstrap.LocationPicker
25058  * @extends Roo.bootstrap.Component
25059  * Bootstrap LocationPicker class
25060  * @cfg {Number} latitude Position when init default 0
25061  * @cfg {Number} longitude Position when init default 0
25062  * @cfg {Number} zoom default 15
25063  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25064  * @cfg {Boolean} mapTypeControl default false
25065  * @cfg {Boolean} disableDoubleClickZoom default false
25066  * @cfg {Boolean} scrollwheel default true
25067  * @cfg {Boolean} streetViewControl default false
25068  * @cfg {Number} radius default 0
25069  * @cfg {String} locationName
25070  * @cfg {Boolean} draggable default true
25071  * @cfg {Boolean} enableAutocomplete default false
25072  * @cfg {Boolean} enableReverseGeocode default true
25073  * @cfg {String} markerTitle
25074  * 
25075  * @constructor
25076  * Create a new LocationPicker
25077  * @param {Object} config The config object
25078  */
25079
25080
25081 Roo.bootstrap.LocationPicker = function(config){
25082     
25083     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25084     
25085     this.addEvents({
25086         /**
25087          * @event initial
25088          * Fires when the picker initialized.
25089          * @param {Roo.bootstrap.LocationPicker} this
25090          * @param {Google Location} location
25091          */
25092         initial : true,
25093         /**
25094          * @event positionchanged
25095          * Fires when the picker position changed.
25096          * @param {Roo.bootstrap.LocationPicker} this
25097          * @param {Google Location} location
25098          */
25099         positionchanged : true,
25100         /**
25101          * @event resize
25102          * Fires when the map resize.
25103          * @param {Roo.bootstrap.LocationPicker} this
25104          */
25105         resize : true,
25106         /**
25107          * @event show
25108          * Fires when the map show.
25109          * @param {Roo.bootstrap.LocationPicker} this
25110          */
25111         show : true,
25112         /**
25113          * @event hide
25114          * Fires when the map hide.
25115          * @param {Roo.bootstrap.LocationPicker} this
25116          */
25117         hide : true,
25118         /**
25119          * @event mapClick
25120          * Fires when click the map.
25121          * @param {Roo.bootstrap.LocationPicker} this
25122          * @param {Map event} e
25123          */
25124         mapClick : true,
25125         /**
25126          * @event mapRightClick
25127          * Fires when right click the map.
25128          * @param {Roo.bootstrap.LocationPicker} this
25129          * @param {Map event} e
25130          */
25131         mapRightClick : true,
25132         /**
25133          * @event markerClick
25134          * Fires when click the marker.
25135          * @param {Roo.bootstrap.LocationPicker} this
25136          * @param {Map event} e
25137          */
25138         markerClick : true,
25139         /**
25140          * @event markerRightClick
25141          * Fires when right click the marker.
25142          * @param {Roo.bootstrap.LocationPicker} this
25143          * @param {Map event} e
25144          */
25145         markerRightClick : true,
25146         /**
25147          * @event OverlayViewDraw
25148          * Fires when OverlayView Draw
25149          * @param {Roo.bootstrap.LocationPicker} this
25150          */
25151         OverlayViewDraw : true,
25152         /**
25153          * @event OverlayViewOnAdd
25154          * Fires when OverlayView Draw
25155          * @param {Roo.bootstrap.LocationPicker} this
25156          */
25157         OverlayViewOnAdd : true,
25158         /**
25159          * @event OverlayViewOnRemove
25160          * Fires when OverlayView Draw
25161          * @param {Roo.bootstrap.LocationPicker} this
25162          */
25163         OverlayViewOnRemove : true,
25164         /**
25165          * @event OverlayViewShow
25166          * Fires when OverlayView Draw
25167          * @param {Roo.bootstrap.LocationPicker} this
25168          * @param {Pixel} cpx
25169          */
25170         OverlayViewShow : true,
25171         /**
25172          * @event OverlayViewHide
25173          * Fires when OverlayView Draw
25174          * @param {Roo.bootstrap.LocationPicker} this
25175          */
25176         OverlayViewHide : true,
25177         /**
25178          * @event loadexception
25179          * Fires when load google lib failed.
25180          * @param {Roo.bootstrap.LocationPicker} this
25181          */
25182         loadexception : true
25183     });
25184         
25185 };
25186
25187 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
25188     
25189     gMapContext: false,
25190     
25191     latitude: 0,
25192     longitude: 0,
25193     zoom: 15,
25194     mapTypeId: false,
25195     mapTypeControl: false,
25196     disableDoubleClickZoom: false,
25197     scrollwheel: true,
25198     streetViewControl: false,
25199     radius: 0,
25200     locationName: '',
25201     draggable: true,
25202     enableAutocomplete: false,
25203     enableReverseGeocode: true,
25204     markerTitle: '',
25205     
25206     getAutoCreate: function()
25207     {
25208
25209         var cfg = {
25210             tag: 'div',
25211             cls: 'roo-location-picker'
25212         };
25213         
25214         return cfg
25215     },
25216     
25217     initEvents: function(ct, position)
25218     {       
25219         if(!this.el.getWidth() || this.isApplied()){
25220             return;
25221         }
25222         
25223         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25224         
25225         this.initial();
25226     },
25227     
25228     initial: function()
25229     {
25230         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
25231             this.fireEvent('loadexception', this);
25232             return;
25233         }
25234         
25235         if(!this.mapTypeId){
25236             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
25237         }
25238         
25239         this.gMapContext = this.GMapContext();
25240         
25241         this.initOverlayView();
25242         
25243         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
25244         
25245         var _this = this;
25246                 
25247         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
25248             _this.setPosition(_this.gMapContext.marker.position);
25249         });
25250         
25251         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
25252             _this.fireEvent('mapClick', this, event);
25253             
25254         });
25255
25256         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
25257             _this.fireEvent('mapRightClick', this, event);
25258             
25259         });
25260         
25261         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
25262             _this.fireEvent('markerClick', this, event);
25263             
25264         });
25265
25266         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
25267             _this.fireEvent('markerRightClick', this, event);
25268             
25269         });
25270         
25271         this.setPosition(this.gMapContext.location);
25272         
25273         this.fireEvent('initial', this, this.gMapContext.location);
25274     },
25275     
25276     initOverlayView: function()
25277     {
25278         var _this = this;
25279         
25280         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
25281             
25282             draw: function()
25283             {
25284                 _this.fireEvent('OverlayViewDraw', _this);
25285             },
25286             
25287             onAdd: function()
25288             {
25289                 _this.fireEvent('OverlayViewOnAdd', _this);
25290             },
25291             
25292             onRemove: function()
25293             {
25294                 _this.fireEvent('OverlayViewOnRemove', _this);
25295             },
25296             
25297             show: function(cpx)
25298             {
25299                 _this.fireEvent('OverlayViewShow', _this, cpx);
25300             },
25301             
25302             hide: function()
25303             {
25304                 _this.fireEvent('OverlayViewHide', _this);
25305             }
25306             
25307         });
25308     },
25309     
25310     fromLatLngToContainerPixel: function(event)
25311     {
25312         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
25313     },
25314     
25315     isApplied: function() 
25316     {
25317         return this.getGmapContext() == false ? false : true;
25318     },
25319     
25320     getGmapContext: function() 
25321     {
25322         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
25323     },
25324     
25325     GMapContext: function() 
25326     {
25327         var position = new google.maps.LatLng(this.latitude, this.longitude);
25328         
25329         var _map = new google.maps.Map(this.el.dom, {
25330             center: position,
25331             zoom: this.zoom,
25332             mapTypeId: this.mapTypeId,
25333             mapTypeControl: this.mapTypeControl,
25334             disableDoubleClickZoom: this.disableDoubleClickZoom,
25335             scrollwheel: this.scrollwheel,
25336             streetViewControl: this.streetViewControl,
25337             locationName: this.locationName,
25338             draggable: this.draggable,
25339             enableAutocomplete: this.enableAutocomplete,
25340             enableReverseGeocode: this.enableReverseGeocode
25341         });
25342         
25343         var _marker = new google.maps.Marker({
25344             position: position,
25345             map: _map,
25346             title: this.markerTitle,
25347             draggable: this.draggable
25348         });
25349         
25350         return {
25351             map: _map,
25352             marker: _marker,
25353             circle: null,
25354             location: position,
25355             radius: this.radius,
25356             locationName: this.locationName,
25357             addressComponents: {
25358                 formatted_address: null,
25359                 addressLine1: null,
25360                 addressLine2: null,
25361                 streetName: null,
25362                 streetNumber: null,
25363                 city: null,
25364                 district: null,
25365                 state: null,
25366                 stateOrProvince: null
25367             },
25368             settings: this,
25369             domContainer: this.el.dom,
25370             geodecoder: new google.maps.Geocoder()
25371         };
25372     },
25373     
25374     drawCircle: function(center, radius, options) 
25375     {
25376         if (this.gMapContext.circle != null) {
25377             this.gMapContext.circle.setMap(null);
25378         }
25379         if (radius > 0) {
25380             radius *= 1;
25381             options = Roo.apply({}, options, {
25382                 strokeColor: "#0000FF",
25383                 strokeOpacity: .35,
25384                 strokeWeight: 2,
25385                 fillColor: "#0000FF",
25386                 fillOpacity: .2
25387             });
25388             
25389             options.map = this.gMapContext.map;
25390             options.radius = radius;
25391             options.center = center;
25392             this.gMapContext.circle = new google.maps.Circle(options);
25393             return this.gMapContext.circle;
25394         }
25395         
25396         return null;
25397     },
25398     
25399     setPosition: function(location) 
25400     {
25401         this.gMapContext.location = location;
25402         this.gMapContext.marker.setPosition(location);
25403         this.gMapContext.map.panTo(location);
25404         this.drawCircle(location, this.gMapContext.radius, {});
25405         
25406         var _this = this;
25407         
25408         if (this.gMapContext.settings.enableReverseGeocode) {
25409             this.gMapContext.geodecoder.geocode({
25410                 latLng: this.gMapContext.location
25411             }, function(results, status) {
25412                 
25413                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25414                     _this.gMapContext.locationName = results[0].formatted_address;
25415                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25416                     
25417                     _this.fireEvent('positionchanged', this, location);
25418                 }
25419             });
25420             
25421             return;
25422         }
25423         
25424         this.fireEvent('positionchanged', this, location);
25425     },
25426     
25427     resize: function()
25428     {
25429         google.maps.event.trigger(this.gMapContext.map, "resize");
25430         
25431         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25432         
25433         this.fireEvent('resize', this);
25434     },
25435     
25436     setPositionByLatLng: function(latitude, longitude)
25437     {
25438         this.setPosition(new google.maps.LatLng(latitude, longitude));
25439     },
25440     
25441     getCurrentPosition: function() 
25442     {
25443         return {
25444             latitude: this.gMapContext.location.lat(),
25445             longitude: this.gMapContext.location.lng()
25446         };
25447     },
25448     
25449     getAddressName: function() 
25450     {
25451         return this.gMapContext.locationName;
25452     },
25453     
25454     getAddressComponents: function() 
25455     {
25456         return this.gMapContext.addressComponents;
25457     },
25458     
25459     address_component_from_google_geocode: function(address_components) 
25460     {
25461         var result = {};
25462         
25463         for (var i = 0; i < address_components.length; i++) {
25464             var component = address_components[i];
25465             if (component.types.indexOf("postal_code") >= 0) {
25466                 result.postalCode = component.short_name;
25467             } else if (component.types.indexOf("street_number") >= 0) {
25468                 result.streetNumber = component.short_name;
25469             } else if (component.types.indexOf("route") >= 0) {
25470                 result.streetName = component.short_name;
25471             } else if (component.types.indexOf("neighborhood") >= 0) {
25472                 result.city = component.short_name;
25473             } else if (component.types.indexOf("locality") >= 0) {
25474                 result.city = component.short_name;
25475             } else if (component.types.indexOf("sublocality") >= 0) {
25476                 result.district = component.short_name;
25477             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25478                 result.stateOrProvince = component.short_name;
25479             } else if (component.types.indexOf("country") >= 0) {
25480                 result.country = component.short_name;
25481             }
25482         }
25483         
25484         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25485         result.addressLine2 = "";
25486         return result;
25487     },
25488     
25489     setZoomLevel: function(zoom)
25490     {
25491         this.gMapContext.map.setZoom(zoom);
25492     },
25493     
25494     show: function()
25495     {
25496         if(!this.el){
25497             return;
25498         }
25499         
25500         this.el.show();
25501         
25502         this.resize();
25503         
25504         this.fireEvent('show', this);
25505     },
25506     
25507     hide: function()
25508     {
25509         if(!this.el){
25510             return;
25511         }
25512         
25513         this.el.hide();
25514         
25515         this.fireEvent('hide', this);
25516     }
25517     
25518 });
25519
25520 Roo.apply(Roo.bootstrap.LocationPicker, {
25521     
25522     OverlayView : function(map, options)
25523     {
25524         options = options || {};
25525         
25526         this.setMap(map);
25527     }
25528     
25529     
25530 });/*
25531  * - LGPL
25532  *
25533  * Alert
25534  * 
25535  */
25536
25537 /**
25538  * @class Roo.bootstrap.Alert
25539  * @extends Roo.bootstrap.Component
25540  * Bootstrap Alert class
25541  * @cfg {String} title The title of alert
25542  * @cfg {String} html The content of alert
25543  * @cfg {String} weight (  success | info | warning | danger )
25544  * @cfg {String} faicon font-awesomeicon
25545  * 
25546  * @constructor
25547  * Create a new alert
25548  * @param {Object} config The config object
25549  */
25550
25551
25552 Roo.bootstrap.Alert = function(config){
25553     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25554     
25555 };
25556
25557 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
25558     
25559     title: '',
25560     html: '',
25561     weight: false,
25562     faicon: false,
25563     
25564     getAutoCreate : function()
25565     {
25566         
25567         var cfg = {
25568             tag : 'div',
25569             cls : 'alert',
25570             cn : [
25571                 {
25572                     tag : 'i',
25573                     cls : 'roo-alert-icon'
25574                     
25575                 },
25576                 {
25577                     tag : 'b',
25578                     cls : 'roo-alert-title',
25579                     html : this.title
25580                 },
25581                 {
25582                     tag : 'span',
25583                     cls : 'roo-alert-text',
25584                     html : this.html
25585                 }
25586             ]
25587         };
25588         
25589         if(this.faicon){
25590             cfg.cn[0].cls += ' fa ' + this.faicon;
25591         }
25592         
25593         if(this.weight){
25594             cfg.cls += ' alert-' + this.weight;
25595         }
25596         
25597         return cfg;
25598     },
25599     
25600     initEvents: function() 
25601     {
25602         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25603     },
25604     
25605     setTitle : function(str)
25606     {
25607         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25608     },
25609     
25610     setText : function(str)
25611     {
25612         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25613     },
25614     
25615     setWeight : function(weight)
25616     {
25617         if(this.weight){
25618             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25619         }
25620         
25621         this.weight = weight;
25622         
25623         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25624     },
25625     
25626     setIcon : function(icon)
25627     {
25628         if(this.faicon){
25629             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25630         }
25631         
25632         this.faicon = icon;
25633         
25634         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25635     },
25636     
25637     hide: function() 
25638     {
25639         this.el.hide();   
25640     },
25641     
25642     show: function() 
25643     {  
25644         this.el.show();   
25645     }
25646     
25647 });
25648
25649  
25650 /*
25651 * Licence: LGPL
25652 */
25653
25654 /**
25655  * @class Roo.bootstrap.UploadCropbox
25656  * @extends Roo.bootstrap.Component
25657  * Bootstrap UploadCropbox class
25658  * @cfg {String} emptyText show when image has been loaded
25659  * @cfg {String} rotateNotify show when image too small to rotate
25660  * @cfg {Number} errorTimeout default 3000
25661  * @cfg {Number} minWidth default 300
25662  * @cfg {Number} minHeight default 300
25663  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25664  * @cfg {Boolean} isDocument (true|false) default false
25665  * @cfg {String} url action url
25666  * @cfg {String} paramName default 'imageUpload'
25667  * @cfg {String} method default POST
25668  * @cfg {Boolean} loadMask (true|false) default true
25669  * @cfg {Boolean} loadingText default 'Loading...'
25670  * 
25671  * @constructor
25672  * Create a new UploadCropbox
25673  * @param {Object} config The config object
25674  */
25675
25676 Roo.bootstrap.UploadCropbox = function(config){
25677     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25678     
25679     this.addEvents({
25680         /**
25681          * @event beforeselectfile
25682          * Fire before select file
25683          * @param {Roo.bootstrap.UploadCropbox} this
25684          */
25685         "beforeselectfile" : true,
25686         /**
25687          * @event initial
25688          * Fire after initEvent
25689          * @param {Roo.bootstrap.UploadCropbox} this
25690          */
25691         "initial" : true,
25692         /**
25693          * @event crop
25694          * Fire after initEvent
25695          * @param {Roo.bootstrap.UploadCropbox} this
25696          * @param {String} data
25697          */
25698         "crop" : true,
25699         /**
25700          * @event prepare
25701          * Fire when preparing the file data
25702          * @param {Roo.bootstrap.UploadCropbox} this
25703          * @param {Object} file
25704          */
25705         "prepare" : true,
25706         /**
25707          * @event exception
25708          * Fire when get exception
25709          * @param {Roo.bootstrap.UploadCropbox} this
25710          * @param {XMLHttpRequest} xhr
25711          */
25712         "exception" : true,
25713         /**
25714          * @event beforeloadcanvas
25715          * Fire before load the canvas
25716          * @param {Roo.bootstrap.UploadCropbox} this
25717          * @param {String} src
25718          */
25719         "beforeloadcanvas" : true,
25720         /**
25721          * @event trash
25722          * Fire when trash image
25723          * @param {Roo.bootstrap.UploadCropbox} this
25724          */
25725         "trash" : true,
25726         /**
25727          * @event download
25728          * Fire when download the image
25729          * @param {Roo.bootstrap.UploadCropbox} this
25730          */
25731         "download" : true,
25732         /**
25733          * @event footerbuttonclick
25734          * Fire when footerbuttonclick
25735          * @param {Roo.bootstrap.UploadCropbox} this
25736          * @param {String} type
25737          */
25738         "footerbuttonclick" : true,
25739         /**
25740          * @event resize
25741          * Fire when resize
25742          * @param {Roo.bootstrap.UploadCropbox} this
25743          */
25744         "resize" : true,
25745         /**
25746          * @event rotate
25747          * Fire when rotate the image
25748          * @param {Roo.bootstrap.UploadCropbox} this
25749          * @param {String} pos
25750          */
25751         "rotate" : true,
25752         /**
25753          * @event inspect
25754          * Fire when inspect the file
25755          * @param {Roo.bootstrap.UploadCropbox} this
25756          * @param {Object} file
25757          */
25758         "inspect" : true,
25759         /**
25760          * @event upload
25761          * Fire when xhr upload the file
25762          * @param {Roo.bootstrap.UploadCropbox} this
25763          * @param {Object} data
25764          */
25765         "upload" : true,
25766         /**
25767          * @event arrange
25768          * Fire when arrange the file data
25769          * @param {Roo.bootstrap.UploadCropbox} this
25770          * @param {Object} formData
25771          */
25772         "arrange" : true
25773     });
25774     
25775     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25776 };
25777
25778 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
25779     
25780     emptyText : 'Click to upload image',
25781     rotateNotify : 'Image is too small to rotate',
25782     errorTimeout : 3000,
25783     scale : 0,
25784     baseScale : 1,
25785     rotate : 0,
25786     dragable : false,
25787     pinching : false,
25788     mouseX : 0,
25789     mouseY : 0,
25790     cropData : false,
25791     minWidth : 300,
25792     minHeight : 300,
25793     file : false,
25794     exif : {},
25795     baseRotate : 1,
25796     cropType : 'image/jpeg',
25797     buttons : false,
25798     canvasLoaded : false,
25799     isDocument : false,
25800     method : 'POST',
25801     paramName : 'imageUpload',
25802     loadMask : true,
25803     loadingText : 'Loading...',
25804     maskEl : false,
25805     
25806     getAutoCreate : function()
25807     {
25808         var cfg = {
25809             tag : 'div',
25810             cls : 'roo-upload-cropbox',
25811             cn : [
25812                 {
25813                     tag : 'input',
25814                     cls : 'roo-upload-cropbox-selector',
25815                     type : 'file'
25816                 },
25817                 {
25818                     tag : 'div',
25819                     cls : 'roo-upload-cropbox-body',
25820                     style : 'cursor:pointer',
25821                     cn : [
25822                         {
25823                             tag : 'div',
25824                             cls : 'roo-upload-cropbox-preview'
25825                         },
25826                         {
25827                             tag : 'div',
25828                             cls : 'roo-upload-cropbox-thumb'
25829                         },
25830                         {
25831                             tag : 'div',
25832                             cls : 'roo-upload-cropbox-empty-notify',
25833                             html : this.emptyText
25834                         },
25835                         {
25836                             tag : 'div',
25837                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25838                             html : this.rotateNotify
25839                         }
25840                     ]
25841                 },
25842                 {
25843                     tag : 'div',
25844                     cls : 'roo-upload-cropbox-footer',
25845                     cn : {
25846                         tag : 'div',
25847                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25848                         cn : []
25849                     }
25850                 }
25851             ]
25852         };
25853         
25854         return cfg;
25855     },
25856     
25857     onRender : function(ct, position)
25858     {
25859         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25860         
25861         if (this.buttons.length) {
25862             
25863             Roo.each(this.buttons, function(bb) {
25864                 
25865                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25866                 
25867                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25868                 
25869             }, this);
25870         }
25871         
25872         if(this.loadMask){
25873             this.maskEl = this.el;
25874         }
25875     },
25876     
25877     initEvents : function()
25878     {
25879         this.urlAPI = (window.createObjectURL && window) || 
25880                                 (window.URL && URL.revokeObjectURL && URL) || 
25881                                 (window.webkitURL && webkitURL);
25882                         
25883         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25884         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25885         
25886         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25887         this.selectorEl.hide();
25888         
25889         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25890         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25891         
25892         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25893         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25894         this.thumbEl.hide();
25895         
25896         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25897         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25898         
25899         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25900         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25901         this.errorEl.hide();
25902         
25903         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25904         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25905         this.footerEl.hide();
25906         
25907         this.setThumbBoxSize();
25908         
25909         this.bind();
25910         
25911         this.resize();
25912         
25913         this.fireEvent('initial', this);
25914     },
25915
25916     bind : function()
25917     {
25918         var _this = this;
25919         
25920         window.addEventListener("resize", function() { _this.resize(); } );
25921         
25922         this.bodyEl.on('click', this.beforeSelectFile, this);
25923         
25924         if(Roo.isTouch){
25925             this.bodyEl.on('touchstart', this.onTouchStart, this);
25926             this.bodyEl.on('touchmove', this.onTouchMove, this);
25927             this.bodyEl.on('touchend', this.onTouchEnd, this);
25928         }
25929         
25930         if(!Roo.isTouch){
25931             this.bodyEl.on('mousedown', this.onMouseDown, this);
25932             this.bodyEl.on('mousemove', this.onMouseMove, this);
25933             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25934             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25935             Roo.get(document).on('mouseup', this.onMouseUp, this);
25936         }
25937         
25938         this.selectorEl.on('change', this.onFileSelected, this);
25939     },
25940     
25941     reset : function()
25942     {    
25943         this.scale = 0;
25944         this.baseScale = 1;
25945         this.rotate = 0;
25946         this.baseRotate = 1;
25947         this.dragable = false;
25948         this.pinching = false;
25949         this.mouseX = 0;
25950         this.mouseY = 0;
25951         this.cropData = false;
25952         this.notifyEl.dom.innerHTML = this.emptyText;
25953         
25954         this.selectorEl.dom.value = '';
25955         
25956     },
25957     
25958     resize : function()
25959     {
25960         if(this.fireEvent('resize', this) != false){
25961             this.setThumbBoxPosition();
25962             this.setCanvasPosition();
25963         }
25964     },
25965     
25966     onFooterButtonClick : function(e, el, o, type)
25967     {
25968         switch (type) {
25969             case 'rotate-left' :
25970                 this.onRotateLeft(e);
25971                 break;
25972             case 'rotate-right' :
25973                 this.onRotateRight(e);
25974                 break;
25975             case 'picture' :
25976                 this.beforeSelectFile(e);
25977                 break;
25978             case 'trash' :
25979                 this.trash(e);
25980                 break;
25981             case 'crop' :
25982                 this.crop(e);
25983                 break;
25984             case 'download' :
25985                 this.download(e);
25986                 break;
25987             default :
25988                 break;
25989         }
25990         
25991         this.fireEvent('footerbuttonclick', this, type);
25992     },
25993     
25994     beforeSelectFile : function(e)
25995     {
25996         e.preventDefault();
25997         
25998         if(this.fireEvent('beforeselectfile', this) != false){
25999             this.selectorEl.dom.click();
26000         }
26001     },
26002     
26003     onFileSelected : function(e)
26004     {
26005         e.preventDefault();
26006         
26007         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26008             return;
26009         }
26010         
26011         var file = this.selectorEl.dom.files[0];
26012         
26013         if(this.fireEvent('inspect', this, file) != false){
26014             this.prepare(file);
26015         }
26016         
26017     },
26018     
26019     trash : function(e)
26020     {
26021         this.fireEvent('trash', this);
26022     },
26023     
26024     download : function(e)
26025     {
26026         this.fireEvent('download', this);
26027     },
26028     
26029     loadCanvas : function(src)
26030     {   
26031         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26032             
26033             this.reset();
26034             
26035             this.imageEl = document.createElement('img');
26036             
26037             var _this = this;
26038             
26039             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26040             
26041             this.imageEl.src = src;
26042         }
26043     },
26044     
26045     onLoadCanvas : function()
26046     {   
26047         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26048         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26049         
26050         this.bodyEl.un('click', this.beforeSelectFile, this);
26051         
26052         this.notifyEl.hide();
26053         this.thumbEl.show();
26054         this.footerEl.show();
26055         
26056         this.baseRotateLevel();
26057         
26058         if(this.isDocument){
26059             this.setThumbBoxSize();
26060         }
26061         
26062         this.setThumbBoxPosition();
26063         
26064         this.baseScaleLevel();
26065         
26066         this.draw();
26067         
26068         this.resize();
26069         
26070         this.canvasLoaded = true;
26071         
26072         if(this.loadMask){
26073             this.maskEl.unmask();
26074         }
26075         
26076     },
26077     
26078     setCanvasPosition : function()
26079     {   
26080         if(!this.canvasEl){
26081             return;
26082         }
26083         
26084         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26085         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26086         
26087         this.previewEl.setLeft(pw);
26088         this.previewEl.setTop(ph);
26089         
26090     },
26091     
26092     onMouseDown : function(e)
26093     {   
26094         e.stopEvent();
26095         
26096         this.dragable = true;
26097         this.pinching = false;
26098         
26099         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26100             this.dragable = false;
26101             return;
26102         }
26103         
26104         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26105         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26106         
26107     },
26108     
26109     onMouseMove : function(e)
26110     {   
26111         e.stopEvent();
26112         
26113         if(!this.canvasLoaded){
26114             return;
26115         }
26116         
26117         if (!this.dragable){
26118             return;
26119         }
26120         
26121         var minX = Math.ceil(this.thumbEl.getLeft(true));
26122         var minY = Math.ceil(this.thumbEl.getTop(true));
26123         
26124         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26125         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26126         
26127         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26128         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26129         
26130         x = x - this.mouseX;
26131         y = y - this.mouseY;
26132         
26133         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26134         var bgY = Math.ceil(y + this.previewEl.getTop(true));
26135         
26136         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26137         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26138         
26139         this.previewEl.setLeft(bgX);
26140         this.previewEl.setTop(bgY);
26141         
26142         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26143         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26144     },
26145     
26146     onMouseUp : function(e)
26147     {   
26148         e.stopEvent();
26149         
26150         this.dragable = false;
26151     },
26152     
26153     onMouseWheel : function(e)
26154     {   
26155         e.stopEvent();
26156         
26157         this.startScale = this.scale;
26158         
26159         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26160         
26161         if(!this.zoomable()){
26162             this.scale = this.startScale;
26163             return;
26164         }
26165         
26166         this.draw();
26167         
26168         return;
26169     },
26170     
26171     zoomable : function()
26172     {
26173         var minScale = this.thumbEl.getWidth() / this.minWidth;
26174         
26175         if(this.minWidth < this.minHeight){
26176             minScale = this.thumbEl.getHeight() / this.minHeight;
26177         }
26178         
26179         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26180         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26181         
26182         if(
26183                 this.isDocument &&
26184                 (this.rotate == 0 || this.rotate == 180) && 
26185                 (
26186                     width > this.imageEl.OriginWidth || 
26187                     height > this.imageEl.OriginHeight ||
26188                     (width < this.minWidth && height < this.minHeight)
26189                 )
26190         ){
26191             return false;
26192         }
26193         
26194         if(
26195                 this.isDocument &&
26196                 (this.rotate == 90 || this.rotate == 270) && 
26197                 (
26198                     width > this.imageEl.OriginWidth || 
26199                     height > this.imageEl.OriginHeight ||
26200                     (width < this.minHeight && height < this.minWidth)
26201                 )
26202         ){
26203             return false;
26204         }
26205         
26206         if(
26207                 !this.isDocument &&
26208                 (this.rotate == 0 || this.rotate == 180) && 
26209                 (
26210                     width < this.minWidth || 
26211                     width > this.imageEl.OriginWidth || 
26212                     height < this.minHeight || 
26213                     height > this.imageEl.OriginHeight
26214                 )
26215         ){
26216             return false;
26217         }
26218         
26219         if(
26220                 !this.isDocument &&
26221                 (this.rotate == 90 || this.rotate == 270) && 
26222                 (
26223                     width < this.minHeight || 
26224                     width > this.imageEl.OriginWidth || 
26225                     height < this.minWidth || 
26226                     height > this.imageEl.OriginHeight
26227                 )
26228         ){
26229             return false;
26230         }
26231         
26232         return true;
26233         
26234     },
26235     
26236     onRotateLeft : function(e)
26237     {   
26238         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26239             
26240             var minScale = this.thumbEl.getWidth() / this.minWidth;
26241             
26242             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26243             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26244             
26245             this.startScale = this.scale;
26246             
26247             while (this.getScaleLevel() < minScale){
26248             
26249                 this.scale = this.scale + 1;
26250                 
26251                 if(!this.zoomable()){
26252                     break;
26253                 }
26254                 
26255                 if(
26256                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26257                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26258                 ){
26259                     continue;
26260                 }
26261                 
26262                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26263
26264                 this.draw();
26265                 
26266                 return;
26267             }
26268             
26269             this.scale = this.startScale;
26270             
26271             this.onRotateFail();
26272             
26273             return false;
26274         }
26275         
26276         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26277
26278         if(this.isDocument){
26279             this.setThumbBoxSize();
26280             this.setThumbBoxPosition();
26281             this.setCanvasPosition();
26282         }
26283         
26284         this.draw();
26285         
26286         this.fireEvent('rotate', this, 'left');
26287         
26288     },
26289     
26290     onRotateRight : function(e)
26291     {
26292         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26293             
26294             var minScale = this.thumbEl.getWidth() / this.minWidth;
26295         
26296             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26297             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26298             
26299             this.startScale = this.scale;
26300             
26301             while (this.getScaleLevel() < minScale){
26302             
26303                 this.scale = this.scale + 1;
26304                 
26305                 if(!this.zoomable()){
26306                     break;
26307                 }
26308                 
26309                 if(
26310                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26311                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26312                 ){
26313                     continue;
26314                 }
26315                 
26316                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26317
26318                 this.draw();
26319                 
26320                 return;
26321             }
26322             
26323             this.scale = this.startScale;
26324             
26325             this.onRotateFail();
26326             
26327             return false;
26328         }
26329         
26330         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26331
26332         if(this.isDocument){
26333             this.setThumbBoxSize();
26334             this.setThumbBoxPosition();
26335             this.setCanvasPosition();
26336         }
26337         
26338         this.draw();
26339         
26340         this.fireEvent('rotate', this, 'right');
26341     },
26342     
26343     onRotateFail : function()
26344     {
26345         this.errorEl.show(true);
26346         
26347         var _this = this;
26348         
26349         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26350     },
26351     
26352     draw : function()
26353     {
26354         this.previewEl.dom.innerHTML = '';
26355         
26356         var canvasEl = document.createElement("canvas");
26357         
26358         var contextEl = canvasEl.getContext("2d");
26359         
26360         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26361         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26362         var center = this.imageEl.OriginWidth / 2;
26363         
26364         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26365             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26366             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26367             center = this.imageEl.OriginHeight / 2;
26368         }
26369         
26370         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26371         
26372         contextEl.translate(center, center);
26373         contextEl.rotate(this.rotate * Math.PI / 180);
26374
26375         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26376         
26377         this.canvasEl = document.createElement("canvas");
26378         
26379         this.contextEl = this.canvasEl.getContext("2d");
26380         
26381         switch (this.rotate) {
26382             case 0 :
26383                 
26384                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26385                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26386                 
26387                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26388                 
26389                 break;
26390             case 90 : 
26391                 
26392                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26393                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26394                 
26395                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26396                     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);
26397                     break;
26398                 }
26399                 
26400                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26401                 
26402                 break;
26403             case 180 :
26404                 
26405                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26406                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26407                 
26408                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26409                     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);
26410                     break;
26411                 }
26412                 
26413                 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);
26414                 
26415                 break;
26416             case 270 :
26417                 
26418                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26419                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26420         
26421                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26422                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26423                     break;
26424                 }
26425                 
26426                 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);
26427                 
26428                 break;
26429             default : 
26430                 break;
26431         }
26432         
26433         this.previewEl.appendChild(this.canvasEl);
26434         
26435         this.setCanvasPosition();
26436     },
26437     
26438     crop : function()
26439     {
26440         if(!this.canvasLoaded){
26441             return;
26442         }
26443         
26444         var imageCanvas = document.createElement("canvas");
26445         
26446         var imageContext = imageCanvas.getContext("2d");
26447         
26448         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26449         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26450         
26451         var center = imageCanvas.width / 2;
26452         
26453         imageContext.translate(center, center);
26454         
26455         imageContext.rotate(this.rotate * Math.PI / 180);
26456         
26457         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26458         
26459         var canvas = document.createElement("canvas");
26460         
26461         var context = canvas.getContext("2d");
26462                 
26463         canvas.width = this.minWidth;
26464         canvas.height = this.minHeight;
26465
26466         switch (this.rotate) {
26467             case 0 :
26468                 
26469                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26470                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26471                 
26472                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26473                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26474                 
26475                 var targetWidth = this.minWidth - 2 * x;
26476                 var targetHeight = this.minHeight - 2 * y;
26477                 
26478                 var scale = 1;
26479                 
26480                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26481                     scale = targetWidth / width;
26482                 }
26483                 
26484                 if(x > 0 && y == 0){
26485                     scale = targetHeight / height;
26486                 }
26487                 
26488                 if(x > 0 && y > 0){
26489                     scale = targetWidth / width;
26490                     
26491                     if(width < height){
26492                         scale = targetHeight / height;
26493                     }
26494                 }
26495                 
26496                 context.scale(scale, scale);
26497                 
26498                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26499                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26500
26501                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26502                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26503
26504                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26505                 
26506                 break;
26507             case 90 : 
26508                 
26509                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26510                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26511                 
26512                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26513                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26514                 
26515                 var targetWidth = this.minWidth - 2 * x;
26516                 var targetHeight = this.minHeight - 2 * y;
26517                 
26518                 var scale = 1;
26519                 
26520                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26521                     scale = targetWidth / width;
26522                 }
26523                 
26524                 if(x > 0 && y == 0){
26525                     scale = targetHeight / height;
26526                 }
26527                 
26528                 if(x > 0 && y > 0){
26529                     scale = targetWidth / width;
26530                     
26531                     if(width < height){
26532                         scale = targetHeight / height;
26533                     }
26534                 }
26535                 
26536                 context.scale(scale, scale);
26537                 
26538                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26539                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26540
26541                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26542                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26543                 
26544                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26545                 
26546                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26547                 
26548                 break;
26549             case 180 :
26550                 
26551                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26552                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26553                 
26554                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26555                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26556                 
26557                 var targetWidth = this.minWidth - 2 * x;
26558                 var targetHeight = this.minHeight - 2 * y;
26559                 
26560                 var scale = 1;
26561                 
26562                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26563                     scale = targetWidth / width;
26564                 }
26565                 
26566                 if(x > 0 && y == 0){
26567                     scale = targetHeight / height;
26568                 }
26569                 
26570                 if(x > 0 && y > 0){
26571                     scale = targetWidth / width;
26572                     
26573                     if(width < height){
26574                         scale = targetHeight / height;
26575                     }
26576                 }
26577                 
26578                 context.scale(scale, scale);
26579                 
26580                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26581                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26582
26583                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26584                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26585
26586                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26587                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26588                 
26589                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26590                 
26591                 break;
26592             case 270 :
26593                 
26594                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26595                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26596                 
26597                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26598                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26599                 
26600                 var targetWidth = this.minWidth - 2 * x;
26601                 var targetHeight = this.minHeight - 2 * y;
26602                 
26603                 var scale = 1;
26604                 
26605                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26606                     scale = targetWidth / width;
26607                 }
26608                 
26609                 if(x > 0 && y == 0){
26610                     scale = targetHeight / height;
26611                 }
26612                 
26613                 if(x > 0 && y > 0){
26614                     scale = targetWidth / width;
26615                     
26616                     if(width < height){
26617                         scale = targetHeight / height;
26618                     }
26619                 }
26620                 
26621                 context.scale(scale, scale);
26622                 
26623                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26624                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26625
26626                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26627                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26628                 
26629                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26630                 
26631                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26632                 
26633                 break;
26634             default : 
26635                 break;
26636         }
26637         
26638         this.cropData = canvas.toDataURL(this.cropType);
26639         
26640         if(this.fireEvent('crop', this, this.cropData) !== false){
26641             this.process(this.file, this.cropData);
26642         }
26643         
26644         return;
26645         
26646     },
26647     
26648     setThumbBoxSize : function()
26649     {
26650         var width, height;
26651         
26652         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26653             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26654             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26655             
26656             this.minWidth = width;
26657             this.minHeight = height;
26658             
26659             if(this.rotate == 90 || this.rotate == 270){
26660                 this.minWidth = height;
26661                 this.minHeight = width;
26662             }
26663         }
26664         
26665         height = 300;
26666         width = Math.ceil(this.minWidth * height / this.minHeight);
26667         
26668         if(this.minWidth > this.minHeight){
26669             width = 300;
26670             height = Math.ceil(this.minHeight * width / this.minWidth);
26671         }
26672         
26673         this.thumbEl.setStyle({
26674             width : width + 'px',
26675             height : height + 'px'
26676         });
26677
26678         return;
26679             
26680     },
26681     
26682     setThumbBoxPosition : function()
26683     {
26684         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26685         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26686         
26687         this.thumbEl.setLeft(x);
26688         this.thumbEl.setTop(y);
26689         
26690     },
26691     
26692     baseRotateLevel : function()
26693     {
26694         this.baseRotate = 1;
26695         
26696         if(
26697                 typeof(this.exif) != 'undefined' &&
26698                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26699                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26700         ){
26701             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26702         }
26703         
26704         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26705         
26706     },
26707     
26708     baseScaleLevel : function()
26709     {
26710         var width, height;
26711         
26712         if(this.isDocument){
26713             
26714             if(this.baseRotate == 6 || this.baseRotate == 8){
26715             
26716                 height = this.thumbEl.getHeight();
26717                 this.baseScale = height / this.imageEl.OriginWidth;
26718
26719                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26720                     width = this.thumbEl.getWidth();
26721                     this.baseScale = width / this.imageEl.OriginHeight;
26722                 }
26723
26724                 return;
26725             }
26726
26727             height = this.thumbEl.getHeight();
26728             this.baseScale = height / this.imageEl.OriginHeight;
26729
26730             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26731                 width = this.thumbEl.getWidth();
26732                 this.baseScale = width / this.imageEl.OriginWidth;
26733             }
26734
26735             return;
26736         }
26737         
26738         if(this.baseRotate == 6 || this.baseRotate == 8){
26739             
26740             width = this.thumbEl.getHeight();
26741             this.baseScale = width / this.imageEl.OriginHeight;
26742             
26743             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26744                 height = this.thumbEl.getWidth();
26745                 this.baseScale = height / this.imageEl.OriginHeight;
26746             }
26747             
26748             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26749                 height = this.thumbEl.getWidth();
26750                 this.baseScale = height / this.imageEl.OriginHeight;
26751                 
26752                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26753                     width = this.thumbEl.getHeight();
26754                     this.baseScale = width / this.imageEl.OriginWidth;
26755                 }
26756             }
26757             
26758             return;
26759         }
26760         
26761         width = this.thumbEl.getWidth();
26762         this.baseScale = width / this.imageEl.OriginWidth;
26763         
26764         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26765             height = this.thumbEl.getHeight();
26766             this.baseScale = height / this.imageEl.OriginHeight;
26767         }
26768         
26769         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26770             
26771             height = this.thumbEl.getHeight();
26772             this.baseScale = height / this.imageEl.OriginHeight;
26773             
26774             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26775                 width = this.thumbEl.getWidth();
26776                 this.baseScale = width / this.imageEl.OriginWidth;
26777             }
26778             
26779         }
26780         
26781         return;
26782     },
26783     
26784     getScaleLevel : function()
26785     {
26786         return this.baseScale * Math.pow(1.1, this.scale);
26787     },
26788     
26789     onTouchStart : function(e)
26790     {
26791         if(!this.canvasLoaded){
26792             this.beforeSelectFile(e);
26793             return;
26794         }
26795         
26796         var touches = e.browserEvent.touches;
26797         
26798         if(!touches){
26799             return;
26800         }
26801         
26802         if(touches.length == 1){
26803             this.onMouseDown(e);
26804             return;
26805         }
26806         
26807         if(touches.length != 2){
26808             return;
26809         }
26810         
26811         var coords = [];
26812         
26813         for(var i = 0, finger; finger = touches[i]; i++){
26814             coords.push(finger.pageX, finger.pageY);
26815         }
26816         
26817         var x = Math.pow(coords[0] - coords[2], 2);
26818         var y = Math.pow(coords[1] - coords[3], 2);
26819         
26820         this.startDistance = Math.sqrt(x + y);
26821         
26822         this.startScale = this.scale;
26823         
26824         this.pinching = true;
26825         this.dragable = false;
26826         
26827     },
26828     
26829     onTouchMove : function(e)
26830     {
26831         if(!this.pinching && !this.dragable){
26832             return;
26833         }
26834         
26835         var touches = e.browserEvent.touches;
26836         
26837         if(!touches){
26838             return;
26839         }
26840         
26841         if(this.dragable){
26842             this.onMouseMove(e);
26843             return;
26844         }
26845         
26846         var coords = [];
26847         
26848         for(var i = 0, finger; finger = touches[i]; i++){
26849             coords.push(finger.pageX, finger.pageY);
26850         }
26851         
26852         var x = Math.pow(coords[0] - coords[2], 2);
26853         var y = Math.pow(coords[1] - coords[3], 2);
26854         
26855         this.endDistance = Math.sqrt(x + y);
26856         
26857         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26858         
26859         if(!this.zoomable()){
26860             this.scale = this.startScale;
26861             return;
26862         }
26863         
26864         this.draw();
26865         
26866     },
26867     
26868     onTouchEnd : function(e)
26869     {
26870         this.pinching = false;
26871         this.dragable = false;
26872         
26873     },
26874     
26875     process : function(file, crop)
26876     {
26877         if(this.loadMask){
26878             this.maskEl.mask(this.loadingText);
26879         }
26880         
26881         this.xhr = new XMLHttpRequest();
26882         
26883         file.xhr = this.xhr;
26884
26885         this.xhr.open(this.method, this.url, true);
26886         
26887         var headers = {
26888             "Accept": "application/json",
26889             "Cache-Control": "no-cache",
26890             "X-Requested-With": "XMLHttpRequest"
26891         };
26892         
26893         for (var headerName in headers) {
26894             var headerValue = headers[headerName];
26895             if (headerValue) {
26896                 this.xhr.setRequestHeader(headerName, headerValue);
26897             }
26898         }
26899         
26900         var _this = this;
26901         
26902         this.xhr.onload = function()
26903         {
26904             _this.xhrOnLoad(_this.xhr);
26905         }
26906         
26907         this.xhr.onerror = function()
26908         {
26909             _this.xhrOnError(_this.xhr);
26910         }
26911         
26912         var formData = new FormData();
26913
26914         formData.append('returnHTML', 'NO');
26915         
26916         if(crop){
26917             formData.append('crop', crop);
26918         }
26919         
26920         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26921             formData.append(this.paramName, file, file.name);
26922         }
26923         
26924         if(typeof(file.filename) != 'undefined'){
26925             formData.append('filename', file.filename);
26926         }
26927         
26928         if(typeof(file.mimetype) != 'undefined'){
26929             formData.append('mimetype', file.mimetype);
26930         }
26931         
26932         if(this.fireEvent('arrange', this, formData) != false){
26933             this.xhr.send(formData);
26934         };
26935     },
26936     
26937     xhrOnLoad : function(xhr)
26938     {
26939         if(this.loadMask){
26940             this.maskEl.unmask();
26941         }
26942         
26943         if (xhr.readyState !== 4) {
26944             this.fireEvent('exception', this, xhr);
26945             return;
26946         }
26947
26948         var response = Roo.decode(xhr.responseText);
26949         
26950         if(!response.success){
26951             this.fireEvent('exception', this, xhr);
26952             return;
26953         }
26954         
26955         var response = Roo.decode(xhr.responseText);
26956         
26957         this.fireEvent('upload', this, response);
26958         
26959     },
26960     
26961     xhrOnError : function()
26962     {
26963         if(this.loadMask){
26964             this.maskEl.unmask();
26965         }
26966         
26967         Roo.log('xhr on error');
26968         
26969         var response = Roo.decode(xhr.responseText);
26970           
26971         Roo.log(response);
26972         
26973     },
26974     
26975     prepare : function(file)
26976     {   
26977         if(this.loadMask){
26978             this.maskEl.mask(this.loadingText);
26979         }
26980         
26981         this.file = false;
26982         this.exif = {};
26983         
26984         if(typeof(file) === 'string'){
26985             this.loadCanvas(file);
26986             return;
26987         }
26988         
26989         if(!file || !this.urlAPI){
26990             return;
26991         }
26992         
26993         this.file = file;
26994         this.cropType = file.type;
26995         
26996         var _this = this;
26997         
26998         if(this.fireEvent('prepare', this, this.file) != false){
26999             
27000             var reader = new FileReader();
27001             
27002             reader.onload = function (e) {
27003                 if (e.target.error) {
27004                     Roo.log(e.target.error);
27005                     return;
27006                 }
27007                 
27008                 var buffer = e.target.result,
27009                     dataView = new DataView(buffer),
27010                     offset = 2,
27011                     maxOffset = dataView.byteLength - 4,
27012                     markerBytes,
27013                     markerLength;
27014                 
27015                 if (dataView.getUint16(0) === 0xffd8) {
27016                     while (offset < maxOffset) {
27017                         markerBytes = dataView.getUint16(offset);
27018                         
27019                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27020                             markerLength = dataView.getUint16(offset + 2) + 2;
27021                             if (offset + markerLength > dataView.byteLength) {
27022                                 Roo.log('Invalid meta data: Invalid segment size.');
27023                                 break;
27024                             }
27025                             
27026                             if(markerBytes == 0xffe1){
27027                                 _this.parseExifData(
27028                                     dataView,
27029                                     offset,
27030                                     markerLength
27031                                 );
27032                             }
27033                             
27034                             offset += markerLength;
27035                             
27036                             continue;
27037                         }
27038                         
27039                         break;
27040                     }
27041                     
27042                 }
27043                 
27044                 var url = _this.urlAPI.createObjectURL(_this.file);
27045                 
27046                 _this.loadCanvas(url);
27047                 
27048                 return;
27049             }
27050             
27051             reader.readAsArrayBuffer(this.file);
27052             
27053         }
27054         
27055     },
27056     
27057     parseExifData : function(dataView, offset, length)
27058     {
27059         var tiffOffset = offset + 10,
27060             littleEndian,
27061             dirOffset;
27062     
27063         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27064             // No Exif data, might be XMP data instead
27065             return;
27066         }
27067         
27068         // Check for the ASCII code for "Exif" (0x45786966):
27069         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27070             // No Exif data, might be XMP data instead
27071             return;
27072         }
27073         if (tiffOffset + 8 > dataView.byteLength) {
27074             Roo.log('Invalid Exif data: Invalid segment size.');
27075             return;
27076         }
27077         // Check for the two null bytes:
27078         if (dataView.getUint16(offset + 8) !== 0x0000) {
27079             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27080             return;
27081         }
27082         // Check the byte alignment:
27083         switch (dataView.getUint16(tiffOffset)) {
27084         case 0x4949:
27085             littleEndian = true;
27086             break;
27087         case 0x4D4D:
27088             littleEndian = false;
27089             break;
27090         default:
27091             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27092             return;
27093         }
27094         // Check for the TIFF tag marker (0x002A):
27095         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27096             Roo.log('Invalid Exif data: Missing TIFF marker.');
27097             return;
27098         }
27099         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27100         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27101         
27102         this.parseExifTags(
27103             dataView,
27104             tiffOffset,
27105             tiffOffset + dirOffset,
27106             littleEndian
27107         );
27108     },
27109     
27110     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27111     {
27112         var tagsNumber,
27113             dirEndOffset,
27114             i;
27115         if (dirOffset + 6 > dataView.byteLength) {
27116             Roo.log('Invalid Exif data: Invalid directory offset.');
27117             return;
27118         }
27119         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27120         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27121         if (dirEndOffset + 4 > dataView.byteLength) {
27122             Roo.log('Invalid Exif data: Invalid directory size.');
27123             return;
27124         }
27125         for (i = 0; i < tagsNumber; i += 1) {
27126             this.parseExifTag(
27127                 dataView,
27128                 tiffOffset,
27129                 dirOffset + 2 + 12 * i, // tag offset
27130                 littleEndian
27131             );
27132         }
27133         // Return the offset to the next directory:
27134         return dataView.getUint32(dirEndOffset, littleEndian);
27135     },
27136     
27137     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
27138     {
27139         var tag = dataView.getUint16(offset, littleEndian);
27140         
27141         this.exif[tag] = this.getExifValue(
27142             dataView,
27143             tiffOffset,
27144             offset,
27145             dataView.getUint16(offset + 2, littleEndian), // tag type
27146             dataView.getUint32(offset + 4, littleEndian), // tag length
27147             littleEndian
27148         );
27149     },
27150     
27151     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27152     {
27153         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27154             tagSize,
27155             dataOffset,
27156             values,
27157             i,
27158             str,
27159             c;
27160     
27161         if (!tagType) {
27162             Roo.log('Invalid Exif data: Invalid tag type.');
27163             return;
27164         }
27165         
27166         tagSize = tagType.size * length;
27167         // Determine if the value is contained in the dataOffset bytes,
27168         // or if the value at the dataOffset is a pointer to the actual data:
27169         dataOffset = tagSize > 4 ?
27170                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27171         if (dataOffset + tagSize > dataView.byteLength) {
27172             Roo.log('Invalid Exif data: Invalid data offset.');
27173             return;
27174         }
27175         if (length === 1) {
27176             return tagType.getValue(dataView, dataOffset, littleEndian);
27177         }
27178         values = [];
27179         for (i = 0; i < length; i += 1) {
27180             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27181         }
27182         
27183         if (tagType.ascii) {
27184             str = '';
27185             // Concatenate the chars:
27186             for (i = 0; i < values.length; i += 1) {
27187                 c = values[i];
27188                 // Ignore the terminating NULL byte(s):
27189                 if (c === '\u0000') {
27190                     break;
27191                 }
27192                 str += c;
27193             }
27194             return str;
27195         }
27196         return values;
27197     }
27198     
27199 });
27200
27201 Roo.apply(Roo.bootstrap.UploadCropbox, {
27202     tags : {
27203         'Orientation': 0x0112
27204     },
27205     
27206     Orientation: {
27207             1: 0, //'top-left',
27208 //            2: 'top-right',
27209             3: 180, //'bottom-right',
27210 //            4: 'bottom-left',
27211 //            5: 'left-top',
27212             6: 90, //'right-top',
27213 //            7: 'right-bottom',
27214             8: 270 //'left-bottom'
27215     },
27216     
27217     exifTagTypes : {
27218         // byte, 8-bit unsigned int:
27219         1: {
27220             getValue: function (dataView, dataOffset) {
27221                 return dataView.getUint8(dataOffset);
27222             },
27223             size: 1
27224         },
27225         // ascii, 8-bit byte:
27226         2: {
27227             getValue: function (dataView, dataOffset) {
27228                 return String.fromCharCode(dataView.getUint8(dataOffset));
27229             },
27230             size: 1,
27231             ascii: true
27232         },
27233         // short, 16 bit int:
27234         3: {
27235             getValue: function (dataView, dataOffset, littleEndian) {
27236                 return dataView.getUint16(dataOffset, littleEndian);
27237             },
27238             size: 2
27239         },
27240         // long, 32 bit int:
27241         4: {
27242             getValue: function (dataView, dataOffset, littleEndian) {
27243                 return dataView.getUint32(dataOffset, littleEndian);
27244             },
27245             size: 4
27246         },
27247         // rational = two long values, first is numerator, second is denominator:
27248         5: {
27249             getValue: function (dataView, dataOffset, littleEndian) {
27250                 return dataView.getUint32(dataOffset, littleEndian) /
27251                     dataView.getUint32(dataOffset + 4, littleEndian);
27252             },
27253             size: 8
27254         },
27255         // slong, 32 bit signed int:
27256         9: {
27257             getValue: function (dataView, dataOffset, littleEndian) {
27258                 return dataView.getInt32(dataOffset, littleEndian);
27259             },
27260             size: 4
27261         },
27262         // srational, two slongs, first is numerator, second is denominator:
27263         10: {
27264             getValue: function (dataView, dataOffset, littleEndian) {
27265                 return dataView.getInt32(dataOffset, littleEndian) /
27266                     dataView.getInt32(dataOffset + 4, littleEndian);
27267             },
27268             size: 8
27269         }
27270     },
27271     
27272     footer : {
27273         STANDARD : [
27274             {
27275                 tag : 'div',
27276                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27277                 action : 'rotate-left',
27278                 cn : [
27279                     {
27280                         tag : 'button',
27281                         cls : 'btn btn-default',
27282                         html : '<i class="fa fa-undo"></i>'
27283                     }
27284                 ]
27285             },
27286             {
27287                 tag : 'div',
27288                 cls : 'btn-group roo-upload-cropbox-picture',
27289                 action : 'picture',
27290                 cn : [
27291                     {
27292                         tag : 'button',
27293                         cls : 'btn btn-default',
27294                         html : '<i class="fa fa-picture-o"></i>'
27295                     }
27296                 ]
27297             },
27298             {
27299                 tag : 'div',
27300                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27301                 action : 'rotate-right',
27302                 cn : [
27303                     {
27304                         tag : 'button',
27305                         cls : 'btn btn-default',
27306                         html : '<i class="fa fa-repeat"></i>'
27307                     }
27308                 ]
27309             }
27310         ],
27311         DOCUMENT : [
27312             {
27313                 tag : 'div',
27314                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27315                 action : 'rotate-left',
27316                 cn : [
27317                     {
27318                         tag : 'button',
27319                         cls : 'btn btn-default',
27320                         html : '<i class="fa fa-undo"></i>'
27321                     }
27322                 ]
27323             },
27324             {
27325                 tag : 'div',
27326                 cls : 'btn-group roo-upload-cropbox-download',
27327                 action : 'download',
27328                 cn : [
27329                     {
27330                         tag : 'button',
27331                         cls : 'btn btn-default',
27332                         html : '<i class="fa fa-download"></i>'
27333                     }
27334                 ]
27335             },
27336             {
27337                 tag : 'div',
27338                 cls : 'btn-group roo-upload-cropbox-crop',
27339                 action : 'crop',
27340                 cn : [
27341                     {
27342                         tag : 'button',
27343                         cls : 'btn btn-default',
27344                         html : '<i class="fa fa-crop"></i>'
27345                     }
27346                 ]
27347             },
27348             {
27349                 tag : 'div',
27350                 cls : 'btn-group roo-upload-cropbox-trash',
27351                 action : 'trash',
27352                 cn : [
27353                     {
27354                         tag : 'button',
27355                         cls : 'btn btn-default',
27356                         html : '<i class="fa fa-trash"></i>'
27357                     }
27358                 ]
27359             },
27360             {
27361                 tag : 'div',
27362                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27363                 action : 'rotate-right',
27364                 cn : [
27365                     {
27366                         tag : 'button',
27367                         cls : 'btn btn-default',
27368                         html : '<i class="fa fa-repeat"></i>'
27369                     }
27370                 ]
27371             }
27372         ],
27373         ROTATOR : [
27374             {
27375                 tag : 'div',
27376                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27377                 action : 'rotate-left',
27378                 cn : [
27379                     {
27380                         tag : 'button',
27381                         cls : 'btn btn-default',
27382                         html : '<i class="fa fa-undo"></i>'
27383                     }
27384                 ]
27385             },
27386             {
27387                 tag : 'div',
27388                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27389                 action : 'rotate-right',
27390                 cn : [
27391                     {
27392                         tag : 'button',
27393                         cls : 'btn btn-default',
27394                         html : '<i class="fa fa-repeat"></i>'
27395                     }
27396                 ]
27397             }
27398         ]
27399     }
27400 });
27401
27402 /*
27403 * Licence: LGPL
27404 */
27405
27406 /**
27407  * @class Roo.bootstrap.DocumentManager
27408  * @extends Roo.bootstrap.Component
27409  * Bootstrap DocumentManager class
27410  * @cfg {String} paramName default 'imageUpload'
27411  * @cfg {String} toolTipName default 'filename'
27412  * @cfg {String} method default POST
27413  * @cfg {String} url action url
27414  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27415  * @cfg {Boolean} multiple multiple upload default true
27416  * @cfg {Number} thumbSize default 300
27417  * @cfg {String} fieldLabel
27418  * @cfg {Number} labelWidth default 4
27419  * @cfg {String} labelAlign (left|top) default left
27420  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27421  * 
27422  * @constructor
27423  * Create a new DocumentManager
27424  * @param {Object} config The config object
27425  */
27426
27427 Roo.bootstrap.DocumentManager = function(config){
27428     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27429     
27430     this.files = [];
27431     this.delegates = [];
27432     
27433     this.addEvents({
27434         /**
27435          * @event initial
27436          * Fire when initial the DocumentManager
27437          * @param {Roo.bootstrap.DocumentManager} this
27438          */
27439         "initial" : true,
27440         /**
27441          * @event inspect
27442          * inspect selected file
27443          * @param {Roo.bootstrap.DocumentManager} this
27444          * @param {File} file
27445          */
27446         "inspect" : true,
27447         /**
27448          * @event exception
27449          * Fire when xhr load exception
27450          * @param {Roo.bootstrap.DocumentManager} this
27451          * @param {XMLHttpRequest} xhr
27452          */
27453         "exception" : true,
27454         /**
27455          * @event afterupload
27456          * Fire when xhr load exception
27457          * @param {Roo.bootstrap.DocumentManager} this
27458          * @param {XMLHttpRequest} xhr
27459          */
27460         "afterupload" : true,
27461         /**
27462          * @event prepare
27463          * prepare the form data
27464          * @param {Roo.bootstrap.DocumentManager} this
27465          * @param {Object} formData
27466          */
27467         "prepare" : true,
27468         /**
27469          * @event remove
27470          * Fire when remove the file
27471          * @param {Roo.bootstrap.DocumentManager} this
27472          * @param {Object} file
27473          */
27474         "remove" : true,
27475         /**
27476          * @event refresh
27477          * Fire after refresh the file
27478          * @param {Roo.bootstrap.DocumentManager} this
27479          */
27480         "refresh" : true,
27481         /**
27482          * @event click
27483          * Fire after click the image
27484          * @param {Roo.bootstrap.DocumentManager} this
27485          * @param {Object} file
27486          */
27487         "click" : true,
27488         /**
27489          * @event edit
27490          * Fire when upload a image and editable set to true
27491          * @param {Roo.bootstrap.DocumentManager} this
27492          * @param {Object} file
27493          */
27494         "edit" : true,
27495         /**
27496          * @event beforeselectfile
27497          * Fire before select file
27498          * @param {Roo.bootstrap.DocumentManager} this
27499          */
27500         "beforeselectfile" : true,
27501         /**
27502          * @event process
27503          * Fire before process file
27504          * @param {Roo.bootstrap.DocumentManager} this
27505          * @param {Object} file
27506          */
27507         "process" : true
27508         
27509     });
27510 };
27511
27512 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
27513     
27514     boxes : 0,
27515     inputName : '',
27516     thumbSize : 300,
27517     multiple : true,
27518     files : false,
27519     method : 'POST',
27520     url : '',
27521     paramName : 'imageUpload',
27522     toolTipName : 'filename',
27523     fieldLabel : '',
27524     labelWidth : 4,
27525     labelAlign : 'left',
27526     editable : true,
27527     delegates : false,
27528     xhr : false, 
27529     
27530     getAutoCreate : function()
27531     {   
27532         var managerWidget = {
27533             tag : 'div',
27534             cls : 'roo-document-manager',
27535             cn : [
27536                 {
27537                     tag : 'input',
27538                     cls : 'roo-document-manager-selector',
27539                     type : 'file'
27540                 },
27541                 {
27542                     tag : 'div',
27543                     cls : 'roo-document-manager-uploader',
27544                     cn : [
27545                         {
27546                             tag : 'div',
27547                             cls : 'roo-document-manager-upload-btn',
27548                             html : '<i class="fa fa-plus"></i>'
27549                         }
27550                     ]
27551                     
27552                 }
27553             ]
27554         };
27555         
27556         var content = [
27557             {
27558                 tag : 'div',
27559                 cls : 'column col-md-12',
27560                 cn : managerWidget
27561             }
27562         ];
27563         
27564         if(this.fieldLabel.length){
27565             
27566             content = [
27567                 {
27568                     tag : 'div',
27569                     cls : 'column col-md-12',
27570                     html : this.fieldLabel
27571                 },
27572                 {
27573                     tag : 'div',
27574                     cls : 'column col-md-12',
27575                     cn : managerWidget
27576                 }
27577             ];
27578
27579             if(this.labelAlign == 'left'){
27580                 content = [
27581                     {
27582                         tag : 'div',
27583                         cls : 'column col-md-' + this.labelWidth,
27584                         html : this.fieldLabel
27585                     },
27586                     {
27587                         tag : 'div',
27588                         cls : 'column col-md-' + (12 - this.labelWidth),
27589                         cn : managerWidget
27590                     }
27591                 ];
27592                 
27593             }
27594         }
27595         
27596         var cfg = {
27597             tag : 'div',
27598             cls : 'row clearfix',
27599             cn : content
27600         };
27601         
27602         return cfg;
27603         
27604     },
27605     
27606     initEvents : function()
27607     {
27608         this.managerEl = this.el.select('.roo-document-manager', true).first();
27609         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27610         
27611         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27612         this.selectorEl.hide();
27613         
27614         if(this.multiple){
27615             this.selectorEl.attr('multiple', 'multiple');
27616         }
27617         
27618         this.selectorEl.on('change', this.onFileSelected, this);
27619         
27620         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27621         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27622         
27623         this.uploader.on('click', this.onUploaderClick, this);
27624         
27625         this.renderProgressDialog();
27626         
27627         var _this = this;
27628         
27629         window.addEventListener("resize", function() { _this.refresh(); } );
27630         
27631         this.fireEvent('initial', this);
27632     },
27633     
27634     renderProgressDialog : function()
27635     {
27636         var _this = this;
27637         
27638         this.progressDialog = new Roo.bootstrap.Modal({
27639             cls : 'roo-document-manager-progress-dialog',
27640             allow_close : false,
27641             title : '',
27642             buttons : [
27643                 {
27644                     name  :'cancel',
27645                     weight : 'danger',
27646                     html : 'Cancel'
27647                 }
27648             ], 
27649             listeners : { 
27650                 btnclick : function() {
27651                     _this.uploadCancel();
27652                     this.hide();
27653                 }
27654             }
27655         });
27656          
27657         this.progressDialog.render(Roo.get(document.body));
27658          
27659         this.progress = new Roo.bootstrap.Progress({
27660             cls : 'roo-document-manager-progress',
27661             active : true,
27662             striped : true
27663         });
27664         
27665         this.progress.render(this.progressDialog.getChildContainer());
27666         
27667         this.progressBar = new Roo.bootstrap.ProgressBar({
27668             cls : 'roo-document-manager-progress-bar',
27669             aria_valuenow : 0,
27670             aria_valuemin : 0,
27671             aria_valuemax : 12,
27672             panel : 'success'
27673         });
27674         
27675         this.progressBar.render(this.progress.getChildContainer());
27676     },
27677     
27678     onUploaderClick : function(e)
27679     {
27680         e.preventDefault();
27681      
27682         if(this.fireEvent('beforeselectfile', this) != false){
27683             this.selectorEl.dom.click();
27684         }
27685         
27686     },
27687     
27688     onFileSelected : function(e)
27689     {
27690         e.preventDefault();
27691         
27692         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27693             return;
27694         }
27695         
27696         Roo.each(this.selectorEl.dom.files, function(file){
27697             if(this.fireEvent('inspect', this, file) != false){
27698                 this.files.push(file);
27699             }
27700         }, this);
27701         
27702         this.queue();
27703         
27704     },
27705     
27706     queue : function()
27707     {
27708         this.selectorEl.dom.value = '';
27709         
27710         if(!this.files.length){
27711             return;
27712         }
27713         
27714         if(this.boxes > 0 && this.files.length > this.boxes){
27715             this.files = this.files.slice(0, this.boxes);
27716         }
27717         
27718         this.uploader.show();
27719         
27720         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27721             this.uploader.hide();
27722         }
27723         
27724         var _this = this;
27725         
27726         var files = [];
27727         
27728         var docs = [];
27729         
27730         Roo.each(this.files, function(file){
27731             
27732             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27733                 var f = this.renderPreview(file);
27734                 files.push(f);
27735                 return;
27736             }
27737             
27738             if(file.type.indexOf('image') != -1){
27739                 this.delegates.push(
27740                     (function(){
27741                         _this.process(file);
27742                     }).createDelegate(this)
27743                 );
27744         
27745                 return;
27746             }
27747             
27748             docs.push(
27749                 (function(){
27750                     _this.process(file);
27751                 }).createDelegate(this)
27752             );
27753             
27754         }, this);
27755         
27756         this.files = files;
27757         
27758         this.delegates = this.delegates.concat(docs);
27759         
27760         if(!this.delegates.length){
27761             this.refresh();
27762             return;
27763         }
27764         
27765         this.progressBar.aria_valuemax = this.delegates.length;
27766         
27767         this.arrange();
27768         
27769         return;
27770     },
27771     
27772     arrange : function()
27773     {
27774         if(!this.delegates.length){
27775             this.progressDialog.hide();
27776             this.refresh();
27777             return;
27778         }
27779         
27780         var delegate = this.delegates.shift();
27781         
27782         this.progressDialog.show();
27783         
27784         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27785         
27786         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27787         
27788         delegate();
27789     },
27790     
27791     refresh : function()
27792     {
27793         this.uploader.show();
27794         
27795         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27796             this.uploader.hide();
27797         }
27798         
27799         Roo.isTouch ? this.closable(false) : this.closable(true);
27800         
27801         this.fireEvent('refresh', this);
27802     },
27803     
27804     onRemove : function(e, el, o)
27805     {
27806         e.preventDefault();
27807         
27808         this.fireEvent('remove', this, o);
27809         
27810     },
27811     
27812     remove : function(o)
27813     {
27814         var files = [];
27815         
27816         Roo.each(this.files, function(file){
27817             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27818                 files.push(file);
27819                 return;
27820             }
27821
27822             o.target.remove();
27823
27824         }, this);
27825         
27826         this.files = files;
27827         
27828         this.refresh();
27829     },
27830     
27831     clear : function()
27832     {
27833         Roo.each(this.files, function(file){
27834             if(!file.target){
27835                 return;
27836             }
27837             
27838             file.target.remove();
27839
27840         }, this);
27841         
27842         this.files = [];
27843         
27844         this.refresh();
27845     },
27846     
27847     onClick : function(e, el, o)
27848     {
27849         e.preventDefault();
27850         
27851         this.fireEvent('click', this, o);
27852         
27853     },
27854     
27855     closable : function(closable)
27856     {
27857         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27858             
27859             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27860             
27861             if(closable){
27862                 el.show();
27863                 return;
27864             }
27865             
27866             el.hide();
27867             
27868         }, this);
27869     },
27870     
27871     xhrOnLoad : function(xhr)
27872     {
27873         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27874             el.remove();
27875         }, this);
27876         
27877         if (xhr.readyState !== 4) {
27878             this.arrange();
27879             this.fireEvent('exception', this, xhr);
27880             return;
27881         }
27882
27883         var response = Roo.decode(xhr.responseText);
27884         
27885         if(!response.success){
27886             this.arrange();
27887             this.fireEvent('exception', this, xhr);
27888             return;
27889         }
27890         
27891         var file = this.renderPreview(response.data);
27892         
27893         this.files.push(file);
27894         
27895         this.arrange();
27896         
27897         this.fireEvent('afterupload', this, xhr);
27898         
27899     },
27900     
27901     xhrOnError : function(xhr)
27902     {
27903         Roo.log('xhr on error');
27904         
27905         var response = Roo.decode(xhr.responseText);
27906           
27907         Roo.log(response);
27908         
27909         this.arrange();
27910     },
27911     
27912     process : function(file)
27913     {
27914         if(this.fireEvent('process', this, file) !== false){
27915             if(this.editable && file.type.indexOf('image') != -1){
27916                 this.fireEvent('edit', this, file);
27917                 return;
27918             }
27919
27920             this.uploadStart(file, false);
27921
27922             return;
27923         }
27924         
27925     },
27926     
27927     uploadStart : function(file, crop)
27928     {
27929         this.xhr = new XMLHttpRequest();
27930         
27931         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27932             this.arrange();
27933             return;
27934         }
27935         
27936         file.xhr = this.xhr;
27937             
27938         this.managerEl.createChild({
27939             tag : 'div',
27940             cls : 'roo-document-manager-loading',
27941             cn : [
27942                 {
27943                     tag : 'div',
27944                     tooltip : file.name,
27945                     cls : 'roo-document-manager-thumb',
27946                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27947                 }
27948             ]
27949
27950         });
27951
27952         this.xhr.open(this.method, this.url, true);
27953         
27954         var headers = {
27955             "Accept": "application/json",
27956             "Cache-Control": "no-cache",
27957             "X-Requested-With": "XMLHttpRequest"
27958         };
27959         
27960         for (var headerName in headers) {
27961             var headerValue = headers[headerName];
27962             if (headerValue) {
27963                 this.xhr.setRequestHeader(headerName, headerValue);
27964             }
27965         }
27966         
27967         var _this = this;
27968         
27969         this.xhr.onload = function()
27970         {
27971             _this.xhrOnLoad(_this.xhr);
27972         }
27973         
27974         this.xhr.onerror = function()
27975         {
27976             _this.xhrOnError(_this.xhr);
27977         }
27978         
27979         var formData = new FormData();
27980
27981         formData.append('returnHTML', 'NO');
27982         
27983         if(crop){
27984             formData.append('crop', crop);
27985         }
27986         
27987         formData.append(this.paramName, file, file.name);
27988         
27989         var options = {
27990             file : file, 
27991             manually : false
27992         };
27993         
27994         if(this.fireEvent('prepare', this, formData, options) != false){
27995             
27996             if(options.manually){
27997                 return;
27998             }
27999             
28000             this.xhr.send(formData);
28001             return;
28002         };
28003         
28004         this.uploadCancel();
28005     },
28006     
28007     uploadCancel : function()
28008     {
28009         if (this.xhr) {
28010             this.xhr.abort();
28011         }
28012         
28013         this.delegates = [];
28014         
28015         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28016             el.remove();
28017         }, this);
28018         
28019         this.arrange();
28020     },
28021     
28022     renderPreview : function(file)
28023     {
28024         if(typeof(file.target) != 'undefined' && file.target){
28025             return file;
28026         }
28027         
28028         var previewEl = this.managerEl.createChild({
28029             tag : 'div',
28030             cls : 'roo-document-manager-preview',
28031             cn : [
28032                 {
28033                     tag : 'div',
28034                     tooltip : file[this.toolTipName],
28035                     cls : 'roo-document-manager-thumb',
28036                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
28037                 },
28038                 {
28039                     tag : 'button',
28040                     cls : 'close',
28041                     html : '<i class="fa fa-times-circle"></i>'
28042                 }
28043             ]
28044         });
28045
28046         var close = previewEl.select('button.close', true).first();
28047
28048         close.on('click', this.onRemove, this, file);
28049
28050         file.target = previewEl;
28051
28052         var image = previewEl.select('img', true).first();
28053         
28054         var _this = this;
28055         
28056         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28057         
28058         image.on('click', this.onClick, this, file);
28059         
28060         return file;
28061         
28062     },
28063     
28064     onPreviewLoad : function(file, image)
28065     {
28066         if(typeof(file.target) == 'undefined' || !file.target){
28067             return;
28068         }
28069         
28070         var width = image.dom.naturalWidth || image.dom.width;
28071         var height = image.dom.naturalHeight || image.dom.height;
28072         
28073         if(width > height){
28074             file.target.addClass('wide');
28075             return;
28076         }
28077         
28078         file.target.addClass('tall');
28079         return;
28080         
28081     },
28082     
28083     uploadFromSource : function(file, crop)
28084     {
28085         this.xhr = new XMLHttpRequest();
28086         
28087         this.managerEl.createChild({
28088             tag : 'div',
28089             cls : 'roo-document-manager-loading',
28090             cn : [
28091                 {
28092                     tag : 'div',
28093                     tooltip : file.name,
28094                     cls : 'roo-document-manager-thumb',
28095                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28096                 }
28097             ]
28098
28099         });
28100
28101         this.xhr.open(this.method, this.url, true);
28102         
28103         var headers = {
28104             "Accept": "application/json",
28105             "Cache-Control": "no-cache",
28106             "X-Requested-With": "XMLHttpRequest"
28107         };
28108         
28109         for (var headerName in headers) {
28110             var headerValue = headers[headerName];
28111             if (headerValue) {
28112                 this.xhr.setRequestHeader(headerName, headerValue);
28113             }
28114         }
28115         
28116         var _this = this;
28117         
28118         this.xhr.onload = function()
28119         {
28120             _this.xhrOnLoad(_this.xhr);
28121         }
28122         
28123         this.xhr.onerror = function()
28124         {
28125             _this.xhrOnError(_this.xhr);
28126         }
28127         
28128         var formData = new FormData();
28129
28130         formData.append('returnHTML', 'NO');
28131         
28132         formData.append('crop', crop);
28133         
28134         if(typeof(file.filename) != 'undefined'){
28135             formData.append('filename', file.filename);
28136         }
28137         
28138         if(typeof(file.mimetype) != 'undefined'){
28139             formData.append('mimetype', file.mimetype);
28140         }
28141         
28142         if(this.fireEvent('prepare', this, formData) != false){
28143             this.xhr.send(formData);
28144         };
28145     }
28146 });
28147
28148 /*
28149 * Licence: LGPL
28150 */
28151
28152 /**
28153  * @class Roo.bootstrap.DocumentViewer
28154  * @extends Roo.bootstrap.Component
28155  * Bootstrap DocumentViewer class
28156  * @cfg {Boolean} showDownload (true|false) show download button (default true)
28157  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28158  * 
28159  * @constructor
28160  * Create a new DocumentViewer
28161  * @param {Object} config The config object
28162  */
28163
28164 Roo.bootstrap.DocumentViewer = function(config){
28165     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28166     
28167     this.addEvents({
28168         /**
28169          * @event initial
28170          * Fire after initEvent
28171          * @param {Roo.bootstrap.DocumentViewer} this
28172          */
28173         "initial" : true,
28174         /**
28175          * @event click
28176          * Fire after click
28177          * @param {Roo.bootstrap.DocumentViewer} this
28178          */
28179         "click" : true,
28180         /**
28181          * @event download
28182          * Fire after download button
28183          * @param {Roo.bootstrap.DocumentViewer} this
28184          */
28185         "download" : true,
28186         /**
28187          * @event trash
28188          * Fire after trash button
28189          * @param {Roo.bootstrap.DocumentViewer} this
28190          */
28191         "trash" : true
28192         
28193     });
28194 };
28195
28196 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
28197     
28198     showDownload : true,
28199     
28200     showTrash : true,
28201     
28202     getAutoCreate : function()
28203     {
28204         var cfg = {
28205             tag : 'div',
28206             cls : 'roo-document-viewer',
28207             cn : [
28208                 {
28209                     tag : 'div',
28210                     cls : 'roo-document-viewer-body',
28211                     cn : [
28212                         {
28213                             tag : 'div',
28214                             cls : 'roo-document-viewer-thumb',
28215                             cn : [
28216                                 {
28217                                     tag : 'img',
28218                                     cls : 'roo-document-viewer-image'
28219                                 }
28220                             ]
28221                         }
28222                     ]
28223                 },
28224                 {
28225                     tag : 'div',
28226                     cls : 'roo-document-viewer-footer',
28227                     cn : {
28228                         tag : 'div',
28229                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
28230                         cn : [
28231                             {
28232                                 tag : 'div',
28233                                 cls : 'btn-group roo-document-viewer-download',
28234                                 cn : [
28235                                     {
28236                                         tag : 'button',
28237                                         cls : 'btn btn-default',
28238                                         html : '<i class="fa fa-download"></i>'
28239                                     }
28240                                 ]
28241                             },
28242                             {
28243                                 tag : 'div',
28244                                 cls : 'btn-group roo-document-viewer-trash',
28245                                 cn : [
28246                                     {
28247                                         tag : 'button',
28248                                         cls : 'btn btn-default',
28249                                         html : '<i class="fa fa-trash"></i>'
28250                                     }
28251                                 ]
28252                             }
28253                         ]
28254                     }
28255                 }
28256             ]
28257         };
28258         
28259         return cfg;
28260     },
28261     
28262     initEvents : function()
28263     {
28264         
28265         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
28266         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
28267         
28268         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
28269         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
28270         
28271         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
28272         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
28273         
28274         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
28275         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
28276         
28277         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
28278         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
28279         
28280         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
28281         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
28282         
28283         this.bodyEl.on('click', this.onClick, this);
28284         this.downloadBtn.on('click', this.onDownload, this);
28285         this.trashBtn.on('click', this.onTrash, this);
28286         
28287         this.downloadBtn.hide();
28288         this.trashBtn.hide();
28289         
28290         if(this.showDownload){
28291             this.downloadBtn.show();
28292         }
28293         
28294         if(this.showTrash){
28295             this.trashBtn.show();
28296         }
28297         
28298         if(!this.showDownload && !this.showTrash) {
28299             this.footerEl.hide();
28300         }
28301         
28302     },
28303     
28304     initial : function()
28305     {
28306 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
28307         
28308         
28309         this.fireEvent('initial', this);
28310         
28311     },
28312     
28313     onClick : function(e)
28314     {
28315         e.preventDefault();
28316         
28317         this.fireEvent('click', this);
28318     },
28319     
28320     onDownload : function(e)
28321     {
28322         e.preventDefault();
28323         
28324         this.fireEvent('download', this);
28325     },
28326     
28327     onTrash : function(e)
28328     {
28329         e.preventDefault();
28330         
28331         this.fireEvent('trash', this);
28332     }
28333     
28334 });
28335 /*
28336  * - LGPL
28337  *
28338  * nav progress bar
28339  * 
28340  */
28341
28342 /**
28343  * @class Roo.bootstrap.NavProgressBar
28344  * @extends Roo.bootstrap.Component
28345  * Bootstrap NavProgressBar class
28346  * 
28347  * @constructor
28348  * Create a new nav progress bar
28349  * @param {Object} config The config object
28350  */
28351
28352 Roo.bootstrap.NavProgressBar = function(config){
28353     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
28354
28355     this.bullets = this.bullets || [];
28356    
28357 //    Roo.bootstrap.NavProgressBar.register(this);
28358      this.addEvents({
28359         /**
28360              * @event changed
28361              * Fires when the active item changes
28362              * @param {Roo.bootstrap.NavProgressBar} this
28363              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28364              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
28365          */
28366         'changed': true
28367      });
28368     
28369 };
28370
28371 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
28372     
28373     bullets : [],
28374     barItems : [],
28375     
28376     getAutoCreate : function()
28377     {
28378         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28379         
28380         cfg = {
28381             tag : 'div',
28382             cls : 'roo-navigation-bar-group',
28383             cn : [
28384                 {
28385                     tag : 'div',
28386                     cls : 'roo-navigation-top-bar'
28387                 },
28388                 {
28389                     tag : 'div',
28390                     cls : 'roo-navigation-bullets-bar',
28391                     cn : [
28392                         {
28393                             tag : 'ul',
28394                             cls : 'roo-navigation-bar'
28395                         }
28396                     ]
28397                 },
28398                 
28399                 {
28400                     tag : 'div',
28401                     cls : 'roo-navigation-bottom-bar'
28402                 }
28403             ]
28404             
28405         };
28406         
28407         return cfg;
28408         
28409     },
28410     
28411     initEvents: function() 
28412     {
28413         
28414     },
28415     
28416     onRender : function(ct, position) 
28417     {
28418         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28419         
28420         if(this.bullets.length){
28421             Roo.each(this.bullets, function(b){
28422                this.addItem(b);
28423             }, this);
28424         }
28425         
28426         this.format();
28427         
28428     },
28429     
28430     addItem : function(cfg)
28431     {
28432         var item = new Roo.bootstrap.NavProgressItem(cfg);
28433         
28434         item.parentId = this.id;
28435         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28436         
28437         if(cfg.html){
28438             var top = new Roo.bootstrap.Element({
28439                 tag : 'div',
28440                 cls : 'roo-navigation-bar-text'
28441             });
28442             
28443             var bottom = new Roo.bootstrap.Element({
28444                 tag : 'div',
28445                 cls : 'roo-navigation-bar-text'
28446             });
28447             
28448             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28449             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28450             
28451             var topText = new Roo.bootstrap.Element({
28452                 tag : 'span',
28453                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28454             });
28455             
28456             var bottomText = new Roo.bootstrap.Element({
28457                 tag : 'span',
28458                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28459             });
28460             
28461             topText.onRender(top.el, null);
28462             bottomText.onRender(bottom.el, null);
28463             
28464             item.topEl = top;
28465             item.bottomEl = bottom;
28466         }
28467         
28468         this.barItems.push(item);
28469         
28470         return item;
28471     },
28472     
28473     getActive : function()
28474     {
28475         var active = false;
28476         
28477         Roo.each(this.barItems, function(v){
28478             
28479             if (!v.isActive()) {
28480                 return;
28481             }
28482             
28483             active = v;
28484             return false;
28485             
28486         });
28487         
28488         return active;
28489     },
28490     
28491     setActiveItem : function(item)
28492     {
28493         var prev = false;
28494         
28495         Roo.each(this.barItems, function(v){
28496             if (v.rid == item.rid) {
28497                 return ;
28498             }
28499             
28500             if (v.isActive()) {
28501                 v.setActive(false);
28502                 prev = v;
28503             }
28504         });
28505
28506         item.setActive(true);
28507         
28508         this.fireEvent('changed', this, item, prev);
28509     },
28510     
28511     getBarItem: function(rid)
28512     {
28513         var ret = false;
28514         
28515         Roo.each(this.barItems, function(e) {
28516             if (e.rid != rid) {
28517                 return;
28518             }
28519             
28520             ret =  e;
28521             return false;
28522         });
28523         
28524         return ret;
28525     },
28526     
28527     indexOfItem : function(item)
28528     {
28529         var index = false;
28530         
28531         Roo.each(this.barItems, function(v, i){
28532             
28533             if (v.rid != item.rid) {
28534                 return;
28535             }
28536             
28537             index = i;
28538             return false
28539         });
28540         
28541         return index;
28542     },
28543     
28544     setActiveNext : function()
28545     {
28546         var i = this.indexOfItem(this.getActive());
28547         
28548         if (i > this.barItems.length) {
28549             return;
28550         }
28551         
28552         this.setActiveItem(this.barItems[i+1]);
28553     },
28554     
28555     setActivePrev : function()
28556     {
28557         var i = this.indexOfItem(this.getActive());
28558         
28559         if (i  < 1) {
28560             return;
28561         }
28562         
28563         this.setActiveItem(this.barItems[i-1]);
28564     },
28565     
28566     format : function()
28567     {
28568         if(!this.barItems.length){
28569             return;
28570         }
28571      
28572         var width = 100 / this.barItems.length;
28573         
28574         Roo.each(this.barItems, function(i){
28575             i.el.setStyle('width', width + '%');
28576             i.topEl.el.setStyle('width', width + '%');
28577             i.bottomEl.el.setStyle('width', width + '%');
28578         }, this);
28579         
28580     }
28581     
28582 });
28583 /*
28584  * - LGPL
28585  *
28586  * Nav Progress Item
28587  * 
28588  */
28589
28590 /**
28591  * @class Roo.bootstrap.NavProgressItem
28592  * @extends Roo.bootstrap.Component
28593  * Bootstrap NavProgressItem class
28594  * @cfg {String} rid the reference id
28595  * @cfg {Boolean} active (true|false) Is item active default false
28596  * @cfg {Boolean} disabled (true|false) Is item active default false
28597  * @cfg {String} html
28598  * @cfg {String} position (top|bottom) text position default bottom
28599  * @cfg {String} icon show icon instead of number
28600  * 
28601  * @constructor
28602  * Create a new NavProgressItem
28603  * @param {Object} config The config object
28604  */
28605 Roo.bootstrap.NavProgressItem = function(config){
28606     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28607     this.addEvents({
28608         // raw events
28609         /**
28610          * @event click
28611          * The raw click event for the entire grid.
28612          * @param {Roo.bootstrap.NavProgressItem} this
28613          * @param {Roo.EventObject} e
28614          */
28615         "click" : true
28616     });
28617    
28618 };
28619
28620 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
28621     
28622     rid : '',
28623     active : false,
28624     disabled : false,
28625     html : '',
28626     position : 'bottom',
28627     icon : false,
28628     
28629     getAutoCreate : function()
28630     {
28631         var iconCls = 'roo-navigation-bar-item-icon';
28632         
28633         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28634         
28635         var cfg = {
28636             tag: 'li',
28637             cls: 'roo-navigation-bar-item',
28638             cn : [
28639                 {
28640                     tag : 'i',
28641                     cls : iconCls
28642                 }
28643             ]
28644         };
28645         
28646         if(this.active){
28647             cfg.cls += ' active';
28648         }
28649         if(this.disabled){
28650             cfg.cls += ' disabled';
28651         }
28652         
28653         return cfg;
28654     },
28655     
28656     disable : function()
28657     {
28658         this.setDisabled(true);
28659     },
28660     
28661     enable : function()
28662     {
28663         this.setDisabled(false);
28664     },
28665     
28666     initEvents: function() 
28667     {
28668         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28669         
28670         this.iconEl.on('click', this.onClick, this);
28671     },
28672     
28673     onClick : function(e)
28674     {
28675         e.preventDefault();
28676         
28677         if(this.disabled){
28678             return;
28679         }
28680         
28681         if(this.fireEvent('click', this, e) === false){
28682             return;
28683         };
28684         
28685         this.parent().setActiveItem(this);
28686     },
28687     
28688     isActive: function () 
28689     {
28690         return this.active;
28691     },
28692     
28693     setActive : function(state)
28694     {
28695         if(this.active == state){
28696             return;
28697         }
28698         
28699         this.active = state;
28700         
28701         if (state) {
28702             this.el.addClass('active');
28703             return;
28704         }
28705         
28706         this.el.removeClass('active');
28707         
28708         return;
28709     },
28710     
28711     setDisabled : function(state)
28712     {
28713         if(this.disabled == state){
28714             return;
28715         }
28716         
28717         this.disabled = state;
28718         
28719         if (state) {
28720             this.el.addClass('disabled');
28721             return;
28722         }
28723         
28724         this.el.removeClass('disabled');
28725     },
28726     
28727     tooltipEl : function()
28728     {
28729         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28730     }
28731 });
28732  
28733
28734  /*
28735  * - LGPL
28736  *
28737  * FieldLabel
28738  * 
28739  */
28740
28741 /**
28742  * @class Roo.bootstrap.FieldLabel
28743  * @extends Roo.bootstrap.Component
28744  * Bootstrap FieldLabel class
28745  * @cfg {String} html contents of the element
28746  * @cfg {String} tag tag of the element default label
28747  * @cfg {String} cls class of the element
28748  * @cfg {String} target label target 
28749  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28750  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28751  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28752  * @cfg {String} iconTooltip default "This field is required"
28753  * 
28754  * @constructor
28755  * Create a new FieldLabel
28756  * @param {Object} config The config object
28757  */
28758
28759 Roo.bootstrap.FieldLabel = function(config){
28760     Roo.bootstrap.Element.superclass.constructor.call(this, config);
28761     
28762     this.addEvents({
28763             /**
28764              * @event invalid
28765              * Fires after the field has been marked as invalid.
28766              * @param {Roo.form.FieldLabel} this
28767              * @param {String} msg The validation message
28768              */
28769             invalid : true,
28770             /**
28771              * @event valid
28772              * Fires after the field has been validated with no errors.
28773              * @param {Roo.form.FieldLabel} this
28774              */
28775             valid : true
28776         });
28777 };
28778
28779 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
28780     
28781     tag: 'label',
28782     cls: '',
28783     html: '',
28784     target: '',
28785     allowBlank : true,
28786     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28787     validClass : 'text-success fa fa-lg fa-check',
28788     iconTooltip : 'This field is required',
28789     
28790     getAutoCreate : function(){
28791         
28792         var cfg = {
28793             tag : this.tag,
28794             cls : 'roo-bootstrap-field-label ' + this.cls,
28795             for : this.target,
28796             cn : [
28797                 {
28798                     tag : 'i',
28799                     cls : '',
28800                     tooltip : this.iconTooltip
28801                 },
28802                 {
28803                     tag : 'span',
28804                     html : this.html
28805                 }
28806             ] 
28807         };
28808         
28809         return cfg;
28810     },
28811     
28812     initEvents: function() 
28813     {
28814         Roo.bootstrap.Element.superclass.initEvents.call(this);
28815         
28816         this.iconEl = this.el.select('i', true).first();
28817         
28818         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28819         
28820         Roo.bootstrap.FieldLabel.register(this);
28821     },
28822     
28823     /**
28824      * Mark this field as valid
28825      */
28826     markValid : function()
28827     {
28828         this.iconEl.show();
28829         
28830         this.iconEl.removeClass(this.invalidClass);
28831         
28832         this.iconEl.addClass(this.validClass);
28833         
28834         this.fireEvent('valid', this);
28835     },
28836     
28837     /**
28838      * Mark this field as invalid
28839      * @param {String} msg The validation message
28840      */
28841     markInvalid : function(msg)
28842     {
28843         this.iconEl.show();
28844         
28845         this.iconEl.removeClass(this.validClass);
28846         
28847         this.iconEl.addClass(this.invalidClass);
28848         
28849         this.fireEvent('invalid', this, msg);
28850     }
28851     
28852    
28853 });
28854
28855 Roo.apply(Roo.bootstrap.FieldLabel, {
28856     
28857     groups: {},
28858     
28859      /**
28860     * register a FieldLabel Group
28861     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28862     */
28863     register : function(label)
28864     {
28865         if(this.groups.hasOwnProperty(label.target)){
28866             return;
28867         }
28868      
28869         this.groups[label.target] = label;
28870         
28871     },
28872     /**
28873     * fetch a FieldLabel Group based on the target
28874     * @param {string} target
28875     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28876     */
28877     get: function(target) {
28878         if (typeof(this.groups[target]) == 'undefined') {
28879             return false;
28880         }
28881         
28882         return this.groups[target] ;
28883     }
28884 });
28885
28886  
28887
28888  /*
28889  * - LGPL
28890  *
28891  * page DateSplitField.
28892  * 
28893  */
28894
28895
28896 /**
28897  * @class Roo.bootstrap.DateSplitField
28898  * @extends Roo.bootstrap.Component
28899  * Bootstrap DateSplitField class
28900  * @cfg {string} fieldLabel - the label associated
28901  * @cfg {Number} labelWidth set the width of label (0-12)
28902  * @cfg {String} labelAlign (top|left)
28903  * @cfg {Boolean} dayAllowBlank (true|false) default false
28904  * @cfg {Boolean} monthAllowBlank (true|false) default false
28905  * @cfg {Boolean} yearAllowBlank (true|false) default false
28906  * @cfg {string} dayPlaceholder 
28907  * @cfg {string} monthPlaceholder
28908  * @cfg {string} yearPlaceholder
28909  * @cfg {string} dayFormat default 'd'
28910  * @cfg {string} monthFormat default 'm'
28911  * @cfg {string} yearFormat default 'Y'
28912
28913  *     
28914  * @constructor
28915  * Create a new DateSplitField
28916  * @param {Object} config The config object
28917  */
28918
28919 Roo.bootstrap.DateSplitField = function(config){
28920     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28921     
28922     this.addEvents({
28923         // raw events
28924          /**
28925          * @event years
28926          * getting the data of years
28927          * @param {Roo.bootstrap.DateSplitField} this
28928          * @param {Object} years
28929          */
28930         "years" : true,
28931         /**
28932          * @event days
28933          * getting the data of days
28934          * @param {Roo.bootstrap.DateSplitField} this
28935          * @param {Object} days
28936          */
28937         "days" : true,
28938         /**
28939          * @event invalid
28940          * Fires after the field has been marked as invalid.
28941          * @param {Roo.form.Field} this
28942          * @param {String} msg The validation message
28943          */
28944         invalid : true,
28945        /**
28946          * @event valid
28947          * Fires after the field has been validated with no errors.
28948          * @param {Roo.form.Field} this
28949          */
28950         valid : true
28951     });
28952 };
28953
28954 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
28955     
28956     fieldLabel : '',
28957     labelAlign : 'top',
28958     labelWidth : 3,
28959     dayAllowBlank : false,
28960     monthAllowBlank : false,
28961     yearAllowBlank : false,
28962     dayPlaceholder : '',
28963     monthPlaceholder : '',
28964     yearPlaceholder : '',
28965     dayFormat : 'd',
28966     monthFormat : 'm',
28967     yearFormat : 'Y',
28968     isFormField : true,
28969     
28970     getAutoCreate : function()
28971     {
28972         var cfg = {
28973             tag : 'div',
28974             cls : 'row roo-date-split-field-group',
28975             cn : [
28976                 {
28977                     tag : 'input',
28978                     type : 'hidden',
28979                     cls : 'form-hidden-field roo-date-split-field-group-value',
28980                     name : this.name
28981                 }
28982             ]
28983         };
28984         
28985         if(this.fieldLabel){
28986             cfg.cn.push({
28987                 tag : 'div',
28988                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28989                 cn : [
28990                     {
28991                         tag : 'label',
28992                         html : this.fieldLabel
28993                     }
28994                 ]
28995             });
28996         }
28997         
28998         Roo.each(['day', 'month', 'year'], function(t){
28999             cfg.cn.push({
29000                 tag : 'div',
29001                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
29002             });
29003         }, this);
29004         
29005         return cfg;
29006     },
29007     
29008     inputEl: function ()
29009     {
29010         return this.el.select('.roo-date-split-field-group-value', true).first();
29011     },
29012     
29013     onRender : function(ct, position) 
29014     {
29015         var _this = this;
29016         
29017         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29018         
29019         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29020         
29021         this.dayField = new Roo.bootstrap.ComboBox({
29022             allowBlank : this.dayAllowBlank,
29023             alwaysQuery : true,
29024             displayField : 'value',
29025             editable : false,
29026             fieldLabel : '',
29027             forceSelection : true,
29028             mode : 'local',
29029             placeholder : this.dayPlaceholder,
29030             selectOnFocus : true,
29031             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29032             triggerAction : 'all',
29033             typeAhead : true,
29034             valueField : 'value',
29035             store : new Roo.data.SimpleStore({
29036                 data : (function() {    
29037                     var days = [];
29038                     _this.fireEvent('days', _this, days);
29039                     return days;
29040                 })(),
29041                 fields : [ 'value' ]
29042             }),
29043             listeners : {
29044                 select : function (_self, record, index)
29045                 {
29046                     _this.setValue(_this.getValue());
29047                 }
29048             }
29049         });
29050
29051         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29052         
29053         this.monthField = new Roo.bootstrap.MonthField({
29054             after : '<i class=\"fa fa-calendar\"></i>',
29055             allowBlank : this.monthAllowBlank,
29056             placeholder : this.monthPlaceholder,
29057             readOnly : true,
29058             listeners : {
29059                 render : function (_self)
29060                 {
29061                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
29062                         e.preventDefault();
29063                         _self.focus();
29064                     });
29065                 },
29066                 select : function (_self, oldvalue, newvalue)
29067                 {
29068                     _this.setValue(_this.getValue());
29069                 }
29070             }
29071         });
29072         
29073         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29074         
29075         this.yearField = new Roo.bootstrap.ComboBox({
29076             allowBlank : this.yearAllowBlank,
29077             alwaysQuery : true,
29078             displayField : 'value',
29079             editable : false,
29080             fieldLabel : '',
29081             forceSelection : true,
29082             mode : 'local',
29083             placeholder : this.yearPlaceholder,
29084             selectOnFocus : true,
29085             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29086             triggerAction : 'all',
29087             typeAhead : true,
29088             valueField : 'value',
29089             store : new Roo.data.SimpleStore({
29090                 data : (function() {
29091                     var years = [];
29092                     _this.fireEvent('years', _this, years);
29093                     return years;
29094                 })(),
29095                 fields : [ 'value' ]
29096             }),
29097             listeners : {
29098                 select : function (_self, record, index)
29099                 {
29100                     _this.setValue(_this.getValue());
29101                 }
29102             }
29103         });
29104
29105         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29106     },
29107     
29108     setValue : function(v, format)
29109     {
29110         this.inputEl.dom.value = v;
29111         
29112         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29113         
29114         var d = Date.parseDate(v, f);
29115         
29116         if(!d){
29117             this.validate();
29118             return;
29119         }
29120         
29121         this.setDay(d.format(this.dayFormat));
29122         this.setMonth(d.format(this.monthFormat));
29123         this.setYear(d.format(this.yearFormat));
29124         
29125         this.validate();
29126         
29127         return;
29128     },
29129     
29130     setDay : function(v)
29131     {
29132         this.dayField.setValue(v);
29133         this.inputEl.dom.value = this.getValue();
29134         this.validate();
29135         return;
29136     },
29137     
29138     setMonth : function(v)
29139     {
29140         this.monthField.setValue(v, true);
29141         this.inputEl.dom.value = this.getValue();
29142         this.validate();
29143         return;
29144     },
29145     
29146     setYear : function(v)
29147     {
29148         this.yearField.setValue(v);
29149         this.inputEl.dom.value = this.getValue();
29150         this.validate();
29151         return;
29152     },
29153     
29154     getDay : function()
29155     {
29156         return this.dayField.getValue();
29157     },
29158     
29159     getMonth : function()
29160     {
29161         return this.monthField.getValue();
29162     },
29163     
29164     getYear : function()
29165     {
29166         return this.yearField.getValue();
29167     },
29168     
29169     getValue : function()
29170     {
29171         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
29172         
29173         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
29174         
29175         return date;
29176     },
29177     
29178     reset : function()
29179     {
29180         this.setDay('');
29181         this.setMonth('');
29182         this.setYear('');
29183         this.inputEl.dom.value = '';
29184         this.validate();
29185         return;
29186     },
29187     
29188     validate : function()
29189     {
29190         var d = this.dayField.validate();
29191         var m = this.monthField.validate();
29192         var y = this.yearField.validate();
29193         
29194         var valid = true;
29195         
29196         if(
29197                 (!this.dayAllowBlank && !d) ||
29198                 (!this.monthAllowBlank && !m) ||
29199                 (!this.yearAllowBlank && !y)
29200         ){
29201             valid = false;
29202         }
29203         
29204         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
29205             return valid;
29206         }
29207         
29208         if(valid){
29209             this.markValid();
29210             return valid;
29211         }
29212         
29213         this.markInvalid();
29214         
29215         return valid;
29216     },
29217     
29218     markValid : function()
29219     {
29220         
29221         var label = this.el.select('label', true).first();
29222         var icon = this.el.select('i.fa-star', true).first();
29223
29224         if(label && icon){
29225             icon.remove();
29226         }
29227         
29228         this.fireEvent('valid', this);
29229     },
29230     
29231      /**
29232      * Mark this field as invalid
29233      * @param {String} msg The validation message
29234      */
29235     markInvalid : function(msg)
29236     {
29237         
29238         var label = this.el.select('label', true).first();
29239         var icon = this.el.select('i.fa-star', true).first();
29240
29241         if(label && !icon){
29242             this.el.select('.roo-date-split-field-label', true).createChild({
29243                 tag : 'i',
29244                 cls : 'text-danger fa fa-lg fa-star',
29245                 tooltip : 'This field is required',
29246                 style : 'margin-right:5px;'
29247             }, label, true);
29248         }
29249         
29250         this.fireEvent('invalid', this, msg);
29251     },
29252     
29253     clearInvalid : function()
29254     {
29255         var label = this.el.select('label', true).first();
29256         var icon = this.el.select('i.fa-star', true).first();
29257
29258         if(label && icon){
29259             icon.remove();
29260         }
29261         
29262         this.fireEvent('valid', this);
29263     },
29264     
29265     getName: function()
29266     {
29267         return this.name;
29268     }
29269     
29270 });
29271
29272  /**
29273  *
29274  * This is based on 
29275  * http://masonry.desandro.com
29276  *
29277  * The idea is to render all the bricks based on vertical width...
29278  *
29279  * The original code extends 'outlayer' - we might need to use that....
29280  * 
29281  */
29282
29283
29284 /**
29285  * @class Roo.bootstrap.LayoutMasonry
29286  * @extends Roo.bootstrap.Component
29287  * Bootstrap Layout Masonry class
29288  * 
29289  * @constructor
29290  * Create a new Element
29291  * @param {Object} config The config object
29292  */
29293
29294 Roo.bootstrap.LayoutMasonry = function(config){
29295     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
29296     
29297     this.bricks = [];
29298     
29299 };
29300
29301 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
29302     
29303     /**
29304      * @cfg {Boolean} isLayoutInstant = no animation?
29305      */   
29306     isLayoutInstant : false, // needed?
29307    
29308     /**
29309      * @cfg {Number} boxWidth  width of the columns
29310      */   
29311     boxWidth : 450,
29312     
29313       /**
29314      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
29315      */   
29316     boxHeight : 0,
29317     
29318     /**
29319      * @cfg {Number} padWidth padding below box..
29320      */   
29321     padWidth : 10, 
29322     
29323     /**
29324      * @cfg {Number} gutter gutter width..
29325      */   
29326     gutter : 10,
29327     
29328      /**
29329      * @cfg {Number} maxCols maximum number of columns
29330      */   
29331     
29332     maxCols: 0,
29333     
29334     /**
29335      * @cfg {Boolean} isAutoInitial defalut true
29336      */   
29337     isAutoInitial : true, 
29338     
29339     containerWidth: 0,
29340     
29341     /**
29342      * @cfg {Boolean} isHorizontal defalut false
29343      */   
29344     isHorizontal : false, 
29345
29346     currentSize : null,
29347     
29348     tag: 'div',
29349     
29350     cls: '',
29351     
29352     bricks: null, //CompositeElement
29353     
29354     cols : 1,
29355     
29356     _isLayoutInited : false,
29357     
29358 //    isAlternative : false, // only use for vertical layout...
29359     
29360     /**
29361      * @cfg {Number} alternativePadWidth padding below box..
29362      */   
29363     alternativePadWidth : 50, 
29364     
29365     getAutoCreate : function(){
29366         
29367         var cfg = {
29368             tag: this.tag,
29369             cls: 'blog-masonary-wrapper ' + this.cls,
29370             cn : {
29371                 cls : 'mas-boxes masonary'
29372             }
29373         };
29374         
29375         return cfg;
29376     },
29377     
29378     getChildContainer: function( )
29379     {
29380         if (this.boxesEl) {
29381             return this.boxesEl;
29382         }
29383         
29384         this.boxesEl = this.el.select('.mas-boxes').first();
29385         
29386         return this.boxesEl;
29387     },
29388     
29389     
29390     initEvents : function()
29391     {
29392         var _this = this;
29393         
29394         if(this.isAutoInitial){
29395             Roo.log('hook children rendered');
29396             this.on('childrenrendered', function() {
29397                 Roo.log('children rendered');
29398                 _this.initial();
29399             } ,this);
29400         }
29401     },
29402     
29403     initial : function()
29404     {
29405         this.currentSize = this.el.getBox(true);
29406         
29407         Roo.EventManager.onWindowResize(this.resize, this); 
29408
29409         if(!this.isAutoInitial){
29410             this.layout();
29411             return;
29412         }
29413         
29414         this.layout();
29415         
29416         return;
29417         //this.layout.defer(500,this);
29418         
29419     },
29420     
29421     resize : function()
29422     {
29423         Roo.log('resize');
29424         
29425         var cs = this.el.getBox(true);
29426         
29427         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29428             Roo.log("no change in with or X");
29429             return;
29430         }
29431         
29432         this.currentSize = cs;
29433         
29434         this.layout();
29435         
29436     },
29437     
29438     layout : function()
29439     {   
29440         this._resetLayout();
29441         
29442         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29443         
29444         this.layoutItems( isInstant );
29445       
29446         this._isLayoutInited = true;
29447         
29448     },
29449     
29450     _resetLayout : function()
29451     {
29452         if(this.isHorizontal){
29453             this.horizontalMeasureColumns();
29454             return;
29455         }
29456         
29457         this.verticalMeasureColumns();
29458         
29459     },
29460     
29461     verticalMeasureColumns : function()
29462     {
29463         this.getContainerWidth();
29464         
29465 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29466 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
29467 //            return;
29468 //        }
29469         
29470         var boxWidth = this.boxWidth + this.padWidth;
29471         
29472         if(this.containerWidth < this.boxWidth){
29473             boxWidth = this.containerWidth
29474         }
29475         
29476         var containerWidth = this.containerWidth;
29477         
29478         var cols = Math.floor(containerWidth / boxWidth);
29479         
29480         this.cols = Math.max( cols, 1 );
29481         
29482         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29483         
29484         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29485         
29486         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29487         
29488         this.colWidth = boxWidth + avail - this.padWidth;
29489         
29490         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29491         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
29492     },
29493     
29494     horizontalMeasureColumns : function()
29495     {
29496         this.getContainerWidth();
29497         
29498         var boxWidth = this.boxWidth;
29499         
29500         if(this.containerWidth < boxWidth){
29501             boxWidth = this.containerWidth;
29502         }
29503         
29504         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29505         
29506         this.el.setHeight(boxWidth);
29507         
29508     },
29509     
29510     getContainerWidth : function()
29511     {
29512         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
29513     },
29514     
29515     layoutItems : function( isInstant )
29516     {
29517         var items = Roo.apply([], this.bricks);
29518         
29519         if(this.isHorizontal){
29520             this._horizontalLayoutItems( items , isInstant );
29521             return;
29522         }
29523         
29524 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29525 //            this._verticalAlternativeLayoutItems( items , isInstant );
29526 //            return;
29527 //        }
29528         
29529         this._verticalLayoutItems( items , isInstant );
29530         
29531     },
29532     
29533     _verticalLayoutItems : function ( items , isInstant)
29534     {
29535         if ( !items || !items.length ) {
29536             return;
29537         }
29538         
29539         var standard = [
29540             ['xs', 'xs', 'xs', 'tall'],
29541             ['xs', 'xs', 'tall'],
29542             ['xs', 'xs', 'sm'],
29543             ['xs', 'xs', 'xs'],
29544             ['xs', 'tall'],
29545             ['xs', 'sm'],
29546             ['xs', 'xs'],
29547             ['xs'],
29548             
29549             ['sm', 'xs', 'xs'],
29550             ['sm', 'xs'],
29551             ['sm'],
29552             
29553             ['tall', 'xs', 'xs', 'xs'],
29554             ['tall', 'xs', 'xs'],
29555             ['tall', 'xs'],
29556             ['tall']
29557             
29558         ];
29559         
29560         var queue = [];
29561         
29562         var boxes = [];
29563         
29564         var box = [];
29565         
29566         Roo.each(items, function(item, k){
29567             
29568             switch (item.size) {
29569                 // these layouts take up a full box,
29570                 case 'md' :
29571                 case 'md-left' :
29572                 case 'md-right' :
29573                 case 'wide' :
29574                     
29575                     if(box.length){
29576                         boxes.push(box);
29577                         box = [];
29578                     }
29579                     
29580                     boxes.push([item]);
29581                     
29582                     break;
29583                     
29584                 case 'xs' :
29585                 case 'sm' :
29586                 case 'tall' :
29587                     
29588                     box.push(item);
29589                     
29590                     break;
29591                 default :
29592                     break;
29593                     
29594             }
29595             
29596         }, this);
29597         
29598         if(box.length){
29599             boxes.push(box);
29600             box = [];
29601         }
29602         
29603         var filterPattern = function(box, length)
29604         {
29605             if(!box.length){
29606                 return;
29607             }
29608             
29609             var match = false;
29610             
29611             var pattern = box.slice(0, length);
29612             
29613             var format = [];
29614             
29615             Roo.each(pattern, function(i){
29616                 format.push(i.size);
29617             }, this);
29618             
29619             Roo.each(standard, function(s){
29620                 
29621                 if(String(s) != String(format)){
29622                     return;
29623                 }
29624                 
29625                 match = true;
29626                 return false;
29627                 
29628             }, this);
29629             
29630             if(!match && length == 1){
29631                 return;
29632             }
29633             
29634             if(!match){
29635                 filterPattern(box, length - 1);
29636                 return;
29637             }
29638                 
29639             queue.push(pattern);
29640
29641             box = box.slice(length, box.length);
29642
29643             filterPattern(box, 4);
29644
29645             return;
29646             
29647         }
29648         
29649         Roo.each(boxes, function(box, k){
29650             
29651             if(!box.length){
29652                 return;
29653             }
29654             
29655             if(box.length == 1){
29656                 queue.push(box);
29657                 return;
29658             }
29659             
29660             filterPattern(box, 4);
29661             
29662         }, this);
29663         
29664         this._processVerticalLayoutQueue( queue, isInstant );
29665         
29666     },
29667     
29668 //    _verticalAlternativeLayoutItems : function( items , isInstant )
29669 //    {
29670 //        if ( !items || !items.length ) {
29671 //            return;
29672 //        }
29673 //
29674 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
29675 //        
29676 //    },
29677     
29678     _horizontalLayoutItems : function ( items , isInstant)
29679     {
29680         if ( !items || !items.length || items.length < 3) {
29681             return;
29682         }
29683         
29684         items.reverse();
29685         
29686         var eItems = items.slice(0, 3);
29687         
29688         items = items.slice(3, items.length);
29689         
29690         var standard = [
29691             ['xs', 'xs', 'xs', 'wide'],
29692             ['xs', 'xs', 'wide'],
29693             ['xs', 'xs', 'sm'],
29694             ['xs', 'xs', 'xs'],
29695             ['xs', 'wide'],
29696             ['xs', 'sm'],
29697             ['xs', 'xs'],
29698             ['xs'],
29699             
29700             ['sm', 'xs', 'xs'],
29701             ['sm', 'xs'],
29702             ['sm'],
29703             
29704             ['wide', 'xs', 'xs', 'xs'],
29705             ['wide', 'xs', 'xs'],
29706             ['wide', 'xs'],
29707             ['wide'],
29708             
29709             ['wide-thin']
29710         ];
29711         
29712         var queue = [];
29713         
29714         var boxes = [];
29715         
29716         var box = [];
29717         
29718         Roo.each(items, function(item, k){
29719             
29720             switch (item.size) {
29721                 case 'md' :
29722                 case 'md-left' :
29723                 case 'md-right' :
29724                 case 'tall' :
29725                     
29726                     if(box.length){
29727                         boxes.push(box);
29728                         box = [];
29729                     }
29730                     
29731                     boxes.push([item]);
29732                     
29733                     break;
29734                     
29735                 case 'xs' :
29736                 case 'sm' :
29737                 case 'wide' :
29738                 case 'wide-thin' :
29739                     
29740                     box.push(item);
29741                     
29742                     break;
29743                 default :
29744                     break;
29745                     
29746             }
29747             
29748         }, this);
29749         
29750         if(box.length){
29751             boxes.push(box);
29752             box = [];
29753         }
29754         
29755         var filterPattern = function(box, length)
29756         {
29757             if(!box.length){
29758                 return;
29759             }
29760             
29761             var match = false;
29762             
29763             var pattern = box.slice(0, length);
29764             
29765             var format = [];
29766             
29767             Roo.each(pattern, function(i){
29768                 format.push(i.size);
29769             }, this);
29770             
29771             Roo.each(standard, function(s){
29772                 
29773                 if(String(s) != String(format)){
29774                     return;
29775                 }
29776                 
29777                 match = true;
29778                 return false;
29779                 
29780             }, this);
29781             
29782             if(!match && length == 1){
29783                 return;
29784             }
29785             
29786             if(!match){
29787                 filterPattern(box, length - 1);
29788                 return;
29789             }
29790                 
29791             queue.push(pattern);
29792
29793             box = box.slice(length, box.length);
29794
29795             filterPattern(box, 4);
29796
29797             return;
29798             
29799         }
29800         
29801         Roo.each(boxes, function(box, k){
29802             
29803             if(!box.length){
29804                 return;
29805             }
29806             
29807             if(box.length == 1){
29808                 queue.push(box);
29809                 return;
29810             }
29811             
29812             filterPattern(box, 4);
29813             
29814         }, this);
29815         
29816         
29817         var prune = [];
29818         
29819         var pos = this.el.getBox(true);
29820         
29821         var minX = pos.x;
29822         
29823         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29824         
29825         var hit_end = false;
29826         
29827         Roo.each(queue, function(box){
29828             
29829             if(hit_end){
29830                 
29831                 Roo.each(box, function(b){
29832                 
29833                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29834                     b.el.hide();
29835
29836                 }, this);
29837
29838                 return;
29839             }
29840             
29841             var mx = 0;
29842             
29843             Roo.each(box, function(b){
29844                 
29845                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29846                 b.el.show();
29847
29848                 mx = Math.max(mx, b.x);
29849                 
29850             }, this);
29851             
29852             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29853             
29854             if(maxX < minX){
29855                 
29856                 Roo.each(box, function(b){
29857                 
29858                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29859                     b.el.hide();
29860                     
29861                 }, this);
29862                 
29863                 hit_end = true;
29864                 
29865                 return;
29866             }
29867             
29868             prune.push(box);
29869             
29870         }, this);
29871         
29872         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29873     },
29874     
29875     /** Sets position of item in DOM
29876     * @param {Element} item
29877     * @param {Number} x - horizontal position
29878     * @param {Number} y - vertical position
29879     * @param {Boolean} isInstant - disables transitions
29880     */
29881     _processVerticalLayoutQueue : function( queue, isInstant )
29882     {
29883         var pos = this.el.getBox(true);
29884         var x = pos.x;
29885         var y = pos.y;
29886         var maxY = [];
29887         
29888         for (var i = 0; i < this.cols; i++){
29889             maxY[i] = pos.y;
29890         }
29891         
29892         Roo.each(queue, function(box, k){
29893             
29894             var col = k % this.cols;
29895             
29896             Roo.each(box, function(b,kk){
29897                 
29898                 b.el.position('absolute');
29899                 
29900                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29901                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29902                 
29903                 if(b.size == 'md-left' || b.size == 'md-right'){
29904                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29905                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29906                 }
29907                 
29908                 b.el.setWidth(width);
29909                 b.el.setHeight(height);
29910                 // iframe?
29911                 b.el.select('iframe',true).setSize(width,height);
29912                 
29913             }, this);
29914             
29915             for (var i = 0; i < this.cols; i++){
29916                 
29917                 if(maxY[i] < maxY[col]){
29918                     col = i;
29919                     continue;
29920                 }
29921                 
29922                 col = Math.min(col, i);
29923                 
29924             }
29925             
29926             x = pos.x + col * (this.colWidth + this.padWidth);
29927             
29928             y = maxY[col];
29929             
29930             var positions = [];
29931             
29932             switch (box.length){
29933                 case 1 :
29934                     positions = this.getVerticalOneBoxColPositions(x, y, box);
29935                     break;
29936                 case 2 :
29937                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
29938                     break;
29939                 case 3 :
29940                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
29941                     break;
29942                 case 4 :
29943                     positions = this.getVerticalFourBoxColPositions(x, y, box);
29944                     break;
29945                 default :
29946                     break;
29947             }
29948             
29949             Roo.each(box, function(b,kk){
29950                 
29951                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29952                 
29953                 var sz = b.el.getSize();
29954                 
29955                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29956                 
29957             }, this);
29958             
29959         }, this);
29960         
29961         var mY = 0;
29962         
29963         for (var i = 0; i < this.cols; i++){
29964             mY = Math.max(mY, maxY[i]);
29965         }
29966         
29967         this.el.setHeight(mY - pos.y);
29968         
29969     },
29970     
29971 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29972 //    {
29973 //        var pos = this.el.getBox(true);
29974 //        var x = pos.x;
29975 //        var y = pos.y;
29976 //        var maxX = pos.right;
29977 //        
29978 //        var maxHeight = 0;
29979 //        
29980 //        Roo.each(items, function(item, k){
29981 //            
29982 //            var c = k % 2;
29983 //            
29984 //            item.el.position('absolute');
29985 //                
29986 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29987 //
29988 //            item.el.setWidth(width);
29989 //
29990 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29991 //
29992 //            item.el.setHeight(height);
29993 //            
29994 //            if(c == 0){
29995 //                item.el.setXY([x, y], isInstant ? false : true);
29996 //            } else {
29997 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
29998 //            }
29999 //            
30000 //            y = y + height + this.alternativePadWidth;
30001 //            
30002 //            maxHeight = maxHeight + height + this.alternativePadWidth;
30003 //            
30004 //        }, this);
30005 //        
30006 //        this.el.setHeight(maxHeight);
30007 //        
30008 //    },
30009     
30010     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30011     {
30012         var pos = this.el.getBox(true);
30013         
30014         var minX = pos.x;
30015         var minY = pos.y;
30016         
30017         var maxX = pos.right;
30018         
30019         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
30020         
30021         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30022         
30023         Roo.each(queue, function(box, k){
30024             
30025             Roo.each(box, function(b, kk){
30026                 
30027                 b.el.position('absolute');
30028                 
30029                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30030                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30031                 
30032                 if(b.size == 'md-left' || b.size == 'md-right'){
30033                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30034                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30035                 }
30036                 
30037                 b.el.setWidth(width);
30038                 b.el.setHeight(height);
30039                 
30040             }, this);
30041             
30042             if(!box.length){
30043                 return;
30044             }
30045             
30046             var positions = [];
30047             
30048             switch (box.length){
30049                 case 1 :
30050                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30051                     break;
30052                 case 2 :
30053                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30054                     break;
30055                 case 3 :
30056                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30057                     break;
30058                 case 4 :
30059                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30060                     break;
30061                 default :
30062                     break;
30063             }
30064             
30065             Roo.each(box, function(b,kk){
30066                 
30067                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30068                 
30069                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30070                 
30071             }, this);
30072             
30073         }, this);
30074         
30075     },
30076     
30077     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30078     {
30079         Roo.each(eItems, function(b,k){
30080             
30081             b.size = (k == 0) ? 'sm' : 'xs';
30082             b.x = (k == 0) ? 2 : 1;
30083             b.y = (k == 0) ? 2 : 1;
30084             
30085             b.el.position('absolute');
30086             
30087             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30088                 
30089             b.el.setWidth(width);
30090             
30091             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30092             
30093             b.el.setHeight(height);
30094             
30095         }, this);
30096
30097         var positions = [];
30098         
30099         positions.push({
30100             x : maxX - this.unitWidth * 2 - this.gutter,
30101             y : minY
30102         });
30103         
30104         positions.push({
30105             x : maxX - this.unitWidth,
30106             y : minY + (this.unitWidth + this.gutter) * 2
30107         });
30108         
30109         positions.push({
30110             x : maxX - this.unitWidth * 3 - this.gutter * 2,
30111             y : minY
30112         });
30113         
30114         Roo.each(eItems, function(b,k){
30115             
30116             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
30117
30118         }, this);
30119         
30120     },
30121     
30122     getVerticalOneBoxColPositions : function(x, y, box)
30123     {
30124         var pos = [];
30125         
30126         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
30127         
30128         if(box[0].size == 'md-left'){
30129             rand = 0;
30130         }
30131         
30132         if(box[0].size == 'md-right'){
30133             rand = 1;
30134         }
30135         
30136         pos.push({
30137             x : x + (this.unitWidth + this.gutter) * rand,
30138             y : y
30139         });
30140         
30141         return pos;
30142     },
30143     
30144     getVerticalTwoBoxColPositions : function(x, y, box)
30145     {
30146         var pos = [];
30147         
30148         if(box[0].size == 'xs'){
30149             
30150             pos.push({
30151                 x : x,
30152                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
30153             });
30154
30155             pos.push({
30156                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
30157                 y : y
30158             });
30159             
30160             return pos;
30161             
30162         }
30163         
30164         pos.push({
30165             x : x,
30166             y : y
30167         });
30168
30169         pos.push({
30170             x : x + (this.unitWidth + this.gutter) * 2,
30171             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
30172         });
30173         
30174         return pos;
30175         
30176     },
30177     
30178     getVerticalThreeBoxColPositions : function(x, y, box)
30179     {
30180         var pos = [];
30181         
30182         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30183             
30184             pos.push({
30185                 x : x,
30186                 y : y
30187             });
30188
30189             pos.push({
30190                 x : x + (this.unitWidth + this.gutter) * 1,
30191                 y : y
30192             });
30193             
30194             pos.push({
30195                 x : x + (this.unitWidth + this.gutter) * 2,
30196                 y : y
30197             });
30198             
30199             return pos;
30200             
30201         }
30202         
30203         if(box[0].size == 'xs' && box[1].size == 'xs'){
30204             
30205             pos.push({
30206                 x : x,
30207                 y : y
30208             });
30209
30210             pos.push({
30211                 x : x,
30212                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
30213             });
30214             
30215             pos.push({
30216                 x : x + (this.unitWidth + this.gutter) * 1,
30217                 y : y
30218             });
30219             
30220             return pos;
30221             
30222         }
30223         
30224         pos.push({
30225             x : x,
30226             y : y
30227         });
30228
30229         pos.push({
30230             x : x + (this.unitWidth + this.gutter) * 2,
30231             y : y
30232         });
30233
30234         pos.push({
30235             x : x + (this.unitWidth + this.gutter) * 2,
30236             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
30237         });
30238             
30239         return pos;
30240         
30241     },
30242     
30243     getVerticalFourBoxColPositions : function(x, y, box)
30244     {
30245         var pos = [];
30246         
30247         if(box[0].size == 'xs'){
30248             
30249             pos.push({
30250                 x : x,
30251                 y : y
30252             });
30253
30254             pos.push({
30255                 x : x,
30256                 y : y + (this.unitHeight + this.gutter) * 1
30257             });
30258             
30259             pos.push({
30260                 x : x,
30261                 y : y + (this.unitHeight + this.gutter) * 2
30262             });
30263             
30264             pos.push({
30265                 x : x + (this.unitWidth + this.gutter) * 1,
30266                 y : y
30267             });
30268             
30269             return pos;
30270             
30271         }
30272         
30273         pos.push({
30274             x : x,
30275             y : y
30276         });
30277
30278         pos.push({
30279             x : x + (this.unitWidth + this.gutter) * 2,
30280             y : y
30281         });
30282
30283         pos.push({
30284             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
30285             y : y + (this.unitHeight + this.gutter) * 1
30286         });
30287
30288         pos.push({
30289             x : x + (this.unitWidth + this.gutter) * 2,
30290             y : y + (this.unitWidth + this.gutter) * 2
30291         });
30292
30293         return pos;
30294         
30295     },
30296     
30297     getHorizontalOneBoxColPositions : function(maxX, minY, box)
30298     {
30299         var pos = [];
30300         
30301         if(box[0].size == 'md-left'){
30302             pos.push({
30303                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30304                 y : minY
30305             });
30306             
30307             return pos;
30308         }
30309         
30310         if(box[0].size == 'md-right'){
30311             pos.push({
30312                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30313                 y : minY + (this.unitWidth + this.gutter) * 1
30314             });
30315             
30316             return pos;
30317         }
30318         
30319         var rand = Math.floor(Math.random() * (4 - box[0].y));
30320         
30321         pos.push({
30322             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30323             y : minY + (this.unitWidth + this.gutter) * rand
30324         });
30325         
30326         return pos;
30327         
30328     },
30329     
30330     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
30331     {
30332         var pos = [];
30333         
30334         if(box[0].size == 'xs'){
30335             
30336             pos.push({
30337                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30338                 y : minY
30339             });
30340
30341             pos.push({
30342                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30343                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
30344             });
30345             
30346             return pos;
30347             
30348         }
30349         
30350         pos.push({
30351             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30352             y : minY
30353         });
30354
30355         pos.push({
30356             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30357             y : minY + (this.unitWidth + this.gutter) * 2
30358         });
30359         
30360         return pos;
30361         
30362     },
30363     
30364     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30365     {
30366         var pos = [];
30367         
30368         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30369             
30370             pos.push({
30371                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30372                 y : minY
30373             });
30374
30375             pos.push({
30376                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30377                 y : minY + (this.unitWidth + this.gutter) * 1
30378             });
30379             
30380             pos.push({
30381                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30382                 y : minY + (this.unitWidth + this.gutter) * 2
30383             });
30384             
30385             return pos;
30386             
30387         }
30388         
30389         if(box[0].size == 'xs' && box[1].size == 'xs'){
30390             
30391             pos.push({
30392                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30393                 y : minY
30394             });
30395
30396             pos.push({
30397                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30398                 y : minY
30399             });
30400             
30401             pos.push({
30402                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30403                 y : minY + (this.unitWidth + this.gutter) * 1
30404             });
30405             
30406             return pos;
30407             
30408         }
30409         
30410         pos.push({
30411             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30412             y : minY
30413         });
30414
30415         pos.push({
30416             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30417             y : minY + (this.unitWidth + this.gutter) * 2
30418         });
30419
30420         pos.push({
30421             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30422             y : minY + (this.unitWidth + this.gutter) * 2
30423         });
30424             
30425         return pos;
30426         
30427     },
30428     
30429     getHorizontalFourBoxColPositions : function(maxX, minY, box)
30430     {
30431         var pos = [];
30432         
30433         if(box[0].size == 'xs'){
30434             
30435             pos.push({
30436                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30437                 y : minY
30438             });
30439
30440             pos.push({
30441                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30442                 y : minY
30443             });
30444             
30445             pos.push({
30446                 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),
30447                 y : minY
30448             });
30449             
30450             pos.push({
30451                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30452                 y : minY + (this.unitWidth + this.gutter) * 1
30453             });
30454             
30455             return pos;
30456             
30457         }
30458         
30459         pos.push({
30460             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30461             y : minY
30462         });
30463         
30464         pos.push({
30465             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30466             y : minY + (this.unitWidth + this.gutter) * 2
30467         });
30468         
30469         pos.push({
30470             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30471             y : minY + (this.unitWidth + this.gutter) * 2
30472         });
30473         
30474         pos.push({
30475             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),
30476             y : minY + (this.unitWidth + this.gutter) * 2
30477         });
30478
30479         return pos;
30480         
30481     }
30482     
30483 });
30484
30485  
30486
30487  /**
30488  *
30489  * This is based on 
30490  * http://masonry.desandro.com
30491  *
30492  * The idea is to render all the bricks based on vertical width...
30493  *
30494  * The original code extends 'outlayer' - we might need to use that....
30495  * 
30496  */
30497
30498
30499 /**
30500  * @class Roo.bootstrap.LayoutMasonryAuto
30501  * @extends Roo.bootstrap.Component
30502  * Bootstrap Layout Masonry class
30503  * 
30504  * @constructor
30505  * Create a new Element
30506  * @param {Object} config The config object
30507  */
30508
30509 Roo.bootstrap.LayoutMasonryAuto = function(config){
30510     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30511 };
30512
30513 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
30514     
30515       /**
30516      * @cfg {Boolean} isFitWidth  - resize the width..
30517      */   
30518     isFitWidth : false,  // options..
30519     /**
30520      * @cfg {Boolean} isOriginLeft = left align?
30521      */   
30522     isOriginLeft : true,
30523     /**
30524      * @cfg {Boolean} isOriginTop = top align?
30525      */   
30526     isOriginTop : false,
30527     /**
30528      * @cfg {Boolean} isLayoutInstant = no animation?
30529      */   
30530     isLayoutInstant : false, // needed?
30531     /**
30532      * @cfg {Boolean} isResizingContainer = not sure if this is used..
30533      */   
30534     isResizingContainer : true,
30535     /**
30536      * @cfg {Number} columnWidth  width of the columns 
30537      */   
30538     
30539     columnWidth : 0,
30540     
30541     /**
30542      * @cfg {Number} maxCols maximum number of columns
30543      */   
30544     
30545     maxCols: 0,
30546     /**
30547      * @cfg {Number} padHeight padding below box..
30548      */   
30549     
30550     padHeight : 10, 
30551     
30552     /**
30553      * @cfg {Boolean} isAutoInitial defalut true
30554      */   
30555     
30556     isAutoInitial : true, 
30557     
30558     // private?
30559     gutter : 0,
30560     
30561     containerWidth: 0,
30562     initialColumnWidth : 0,
30563     currentSize : null,
30564     
30565     colYs : null, // array.
30566     maxY : 0,
30567     padWidth: 10,
30568     
30569     
30570     tag: 'div',
30571     cls: '',
30572     bricks: null, //CompositeElement
30573     cols : 0, // array?
30574     // element : null, // wrapped now this.el
30575     _isLayoutInited : null, 
30576     
30577     
30578     getAutoCreate : function(){
30579         
30580         var cfg = {
30581             tag: this.tag,
30582             cls: 'blog-masonary-wrapper ' + this.cls,
30583             cn : {
30584                 cls : 'mas-boxes masonary'
30585             }
30586         };
30587         
30588         return cfg;
30589     },
30590     
30591     getChildContainer: function( )
30592     {
30593         if (this.boxesEl) {
30594             return this.boxesEl;
30595         }
30596         
30597         this.boxesEl = this.el.select('.mas-boxes').first();
30598         
30599         return this.boxesEl;
30600     },
30601     
30602     
30603     initEvents : function()
30604     {
30605         var _this = this;
30606         
30607         if(this.isAutoInitial){
30608             Roo.log('hook children rendered');
30609             this.on('childrenrendered', function() {
30610                 Roo.log('children rendered');
30611                 _this.initial();
30612             } ,this);
30613         }
30614         
30615     },
30616     
30617     initial : function()
30618     {
30619         this.reloadItems();
30620
30621         this.currentSize = this.el.getBox(true);
30622
30623         /// was window resize... - let's see if this works..
30624         Roo.EventManager.onWindowResize(this.resize, this); 
30625
30626         if(!this.isAutoInitial){
30627             this.layout();
30628             return;
30629         }
30630         
30631         this.layout.defer(500,this);
30632     },
30633     
30634     reloadItems: function()
30635     {
30636         this.bricks = this.el.select('.masonry-brick', true);
30637         
30638         this.bricks.each(function(b) {
30639             //Roo.log(b.getSize());
30640             if (!b.attr('originalwidth')) {
30641                 b.attr('originalwidth',  b.getSize().width);
30642             }
30643             
30644         });
30645         
30646         Roo.log(this.bricks.elements.length);
30647     },
30648     
30649     resize : function()
30650     {
30651         Roo.log('resize');
30652         var cs = this.el.getBox(true);
30653         
30654         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30655             Roo.log("no change in with or X");
30656             return;
30657         }
30658         this.currentSize = cs;
30659         this.layout();
30660     },
30661     
30662     layout : function()
30663     {
30664          Roo.log('layout');
30665         this._resetLayout();
30666         //this._manageStamps();
30667       
30668         // don't animate first layout
30669         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30670         this.layoutItems( isInstant );
30671       
30672         // flag for initalized
30673         this._isLayoutInited = true;
30674     },
30675     
30676     layoutItems : function( isInstant )
30677     {
30678         //var items = this._getItemsForLayout( this.items );
30679         // original code supports filtering layout items.. we just ignore it..
30680         
30681         this._layoutItems( this.bricks , isInstant );
30682       
30683         this._postLayout();
30684     },
30685     _layoutItems : function ( items , isInstant)
30686     {
30687        //this.fireEvent( 'layout', this, items );
30688     
30689
30690         if ( !items || !items.elements.length ) {
30691           // no items, emit event with empty array
30692             return;
30693         }
30694
30695         var queue = [];
30696         items.each(function(item) {
30697             Roo.log("layout item");
30698             Roo.log(item);
30699             // get x/y object from method
30700             var position = this._getItemLayoutPosition( item );
30701             // enqueue
30702             position.item = item;
30703             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30704             queue.push( position );
30705         }, this);
30706       
30707         this._processLayoutQueue( queue );
30708     },
30709     /** Sets position of item in DOM
30710     * @param {Element} item
30711     * @param {Number} x - horizontal position
30712     * @param {Number} y - vertical position
30713     * @param {Boolean} isInstant - disables transitions
30714     */
30715     _processLayoutQueue : function( queue )
30716     {
30717         for ( var i=0, len = queue.length; i < len; i++ ) {
30718             var obj = queue[i];
30719             obj.item.position('absolute');
30720             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30721         }
30722     },
30723       
30724     
30725     /**
30726     * Any logic you want to do after each layout,
30727     * i.e. size the container
30728     */
30729     _postLayout : function()
30730     {
30731         this.resizeContainer();
30732     },
30733     
30734     resizeContainer : function()
30735     {
30736         if ( !this.isResizingContainer ) {
30737             return;
30738         }
30739         var size = this._getContainerSize();
30740         if ( size ) {
30741             this.el.setSize(size.width,size.height);
30742             this.boxesEl.setSize(size.width,size.height);
30743         }
30744     },
30745     
30746     
30747     
30748     _resetLayout : function()
30749     {
30750         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30751         this.colWidth = this.el.getWidth();
30752         //this.gutter = this.el.getWidth(); 
30753         
30754         this.measureColumns();
30755
30756         // reset column Y
30757         var i = this.cols;
30758         this.colYs = [];
30759         while (i--) {
30760             this.colYs.push( 0 );
30761         }
30762     
30763         this.maxY = 0;
30764     },
30765
30766     measureColumns : function()
30767     {
30768         this.getContainerWidth();
30769       // if columnWidth is 0, default to outerWidth of first item
30770         if ( !this.columnWidth ) {
30771             var firstItem = this.bricks.first();
30772             Roo.log(firstItem);
30773             this.columnWidth  = this.containerWidth;
30774             if (firstItem && firstItem.attr('originalwidth') ) {
30775                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30776             }
30777             // columnWidth fall back to item of first element
30778             Roo.log("set column width?");
30779                         this.initialColumnWidth = this.columnWidth  ;
30780
30781             // if first elem has no width, default to size of container
30782             
30783         }
30784         
30785         
30786         if (this.initialColumnWidth) {
30787             this.columnWidth = this.initialColumnWidth;
30788         }
30789         
30790         
30791             
30792         // column width is fixed at the top - however if container width get's smaller we should
30793         // reduce it...
30794         
30795         // this bit calcs how man columns..
30796             
30797         var columnWidth = this.columnWidth += this.gutter;
30798       
30799         // calculate columns
30800         var containerWidth = this.containerWidth + this.gutter;
30801         
30802         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30803         // fix rounding errors, typically with gutters
30804         var excess = columnWidth - containerWidth % columnWidth;
30805         
30806         
30807         // if overshoot is less than a pixel, round up, otherwise floor it
30808         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30809         cols = Math[ mathMethod ]( cols );
30810         this.cols = Math.max( cols, 1 );
30811         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30812         
30813          // padding positioning..
30814         var totalColWidth = this.cols * this.columnWidth;
30815         var padavail = this.containerWidth - totalColWidth;
30816         // so for 2 columns - we need 3 'pads'
30817         
30818         var padNeeded = (1+this.cols) * this.padWidth;
30819         
30820         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30821         
30822         this.columnWidth += padExtra
30823         //this.padWidth = Math.floor(padavail /  ( this.cols));
30824         
30825         // adjust colum width so that padding is fixed??
30826         
30827         // we have 3 columns ... total = width * 3
30828         // we have X left over... that should be used by 
30829         
30830         //if (this.expandC) {
30831             
30832         //}
30833         
30834         
30835         
30836     },
30837     
30838     getContainerWidth : function()
30839     {
30840        /* // container is parent if fit width
30841         var container = this.isFitWidth ? this.element.parentNode : this.element;
30842         // check that this.size and size are there
30843         // IE8 triggers resize on body size change, so they might not be
30844         
30845         var size = getSize( container );  //FIXME
30846         this.containerWidth = size && size.innerWidth; //FIXME
30847         */
30848          
30849         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30850         
30851     },
30852     
30853     _getItemLayoutPosition : function( item )  // what is item?
30854     {
30855         // we resize the item to our columnWidth..
30856       
30857         item.setWidth(this.columnWidth);
30858         item.autoBoxAdjust  = false;
30859         
30860         var sz = item.getSize();
30861  
30862         // how many columns does this brick span
30863         var remainder = this.containerWidth % this.columnWidth;
30864         
30865         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30866         // round if off by 1 pixel, otherwise use ceil
30867         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
30868         colSpan = Math.min( colSpan, this.cols );
30869         
30870         // normally this should be '1' as we dont' currently allow multi width columns..
30871         
30872         var colGroup = this._getColGroup( colSpan );
30873         // get the minimum Y value from the columns
30874         var minimumY = Math.min.apply( Math, colGroup );
30875         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30876         
30877         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
30878          
30879         // position the brick
30880         var position = {
30881             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30882             y: this.currentSize.y + minimumY + this.padHeight
30883         };
30884         
30885         Roo.log(position);
30886         // apply setHeight to necessary columns
30887         var setHeight = minimumY + sz.height + this.padHeight;
30888         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30889         
30890         var setSpan = this.cols + 1 - colGroup.length;
30891         for ( var i = 0; i < setSpan; i++ ) {
30892           this.colYs[ shortColIndex + i ] = setHeight ;
30893         }
30894       
30895         return position;
30896     },
30897     
30898     /**
30899      * @param {Number} colSpan - number of columns the element spans
30900      * @returns {Array} colGroup
30901      */
30902     _getColGroup : function( colSpan )
30903     {
30904         if ( colSpan < 2 ) {
30905           // if brick spans only one column, use all the column Ys
30906           return this.colYs;
30907         }
30908       
30909         var colGroup = [];
30910         // how many different places could this brick fit horizontally
30911         var groupCount = this.cols + 1 - colSpan;
30912         // for each group potential horizontal position
30913         for ( var i = 0; i < groupCount; i++ ) {
30914           // make an array of colY values for that one group
30915           var groupColYs = this.colYs.slice( i, i + colSpan );
30916           // and get the max value of the array
30917           colGroup[i] = Math.max.apply( Math, groupColYs );
30918         }
30919         return colGroup;
30920     },
30921     /*
30922     _manageStamp : function( stamp )
30923     {
30924         var stampSize =  stamp.getSize();
30925         var offset = stamp.getBox();
30926         // get the columns that this stamp affects
30927         var firstX = this.isOriginLeft ? offset.x : offset.right;
30928         var lastX = firstX + stampSize.width;
30929         var firstCol = Math.floor( firstX / this.columnWidth );
30930         firstCol = Math.max( 0, firstCol );
30931         
30932         var lastCol = Math.floor( lastX / this.columnWidth );
30933         // lastCol should not go over if multiple of columnWidth #425
30934         lastCol -= lastX % this.columnWidth ? 0 : 1;
30935         lastCol = Math.min( this.cols - 1, lastCol );
30936         
30937         // set colYs to bottom of the stamp
30938         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30939             stampSize.height;
30940             
30941         for ( var i = firstCol; i <= lastCol; i++ ) {
30942           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30943         }
30944     },
30945     */
30946     
30947     _getContainerSize : function()
30948     {
30949         this.maxY = Math.max.apply( Math, this.colYs );
30950         var size = {
30951             height: this.maxY
30952         };
30953       
30954         if ( this.isFitWidth ) {
30955             size.width = this._getContainerFitWidth();
30956         }
30957       
30958         return size;
30959     },
30960     
30961     _getContainerFitWidth : function()
30962     {
30963         var unusedCols = 0;
30964         // count unused columns
30965         var i = this.cols;
30966         while ( --i ) {
30967           if ( this.colYs[i] !== 0 ) {
30968             break;
30969           }
30970           unusedCols++;
30971         }
30972         // fit container to columns that have been used
30973         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30974     },
30975     
30976     needsResizeLayout : function()
30977     {
30978         var previousWidth = this.containerWidth;
30979         this.getContainerWidth();
30980         return previousWidth !== this.containerWidth;
30981     }
30982  
30983 });
30984
30985  
30986
30987  /*
30988  * - LGPL
30989  *
30990  * element
30991  * 
30992  */
30993
30994 /**
30995  * @class Roo.bootstrap.MasonryBrick
30996  * @extends Roo.bootstrap.Component
30997  * Bootstrap MasonryBrick class
30998  * 
30999  * @constructor
31000  * Create a new MasonryBrick
31001  * @param {Object} config The config object
31002  */
31003
31004 Roo.bootstrap.MasonryBrick = function(config){
31005     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
31006     
31007     this.addEvents({
31008         // raw events
31009         /**
31010          * @event click
31011          * When a MasonryBrick is clcik
31012          * @param {Roo.bootstrap.MasonryBrick} this
31013          * @param {Roo.EventObject} e
31014          */
31015         "click" : true
31016     });
31017 };
31018
31019 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
31020     
31021     /**
31022      * @cfg {String} title
31023      */   
31024     title : '',
31025     /**
31026      * @cfg {String} html
31027      */   
31028     html : '',
31029     /**
31030      * @cfg {String} bgimage
31031      */   
31032     bgimage : '',
31033     /**
31034      * @cfg {String} videourl
31035      */   
31036     videourl : '',
31037     /**
31038      * @cfg {String} cls
31039      */   
31040     cls : '',
31041     /**
31042      * @cfg {String} href
31043      */   
31044     href : '',
31045     /**
31046      * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
31047      */   
31048     size : 'xs',
31049     
31050     /**
31051      * @cfg {String} (center|bottom) placetitle
31052      */   
31053     placetitle : '',
31054     
31055     /**
31056      * @cfg {Boolean} isFitContainer defalut true
31057      */   
31058     isFitContainer : true, 
31059     
31060     /**
31061      * @cfg {Boolean} preventDefault defalut false
31062      */   
31063     preventDefault : false, 
31064     
31065     getAutoCreate : function()
31066     {
31067         if(!this.isFitContainer){
31068             return this.getSplitAutoCreate();
31069         }
31070         
31071         var cls = 'masonry-brick masonry-brick-full';
31072         
31073         if(this.href.length){
31074             cls += ' masonry-brick-link';
31075         }
31076         
31077         if(this.bgimage.length){
31078             cls += ' masonry-brick-image';
31079         }
31080         
31081         if(!this.html.length){
31082             cls += ' enable-mask';
31083         }
31084         
31085         if(this.size){
31086             cls += ' masonry-' + this.size + '-brick';
31087         }
31088         
31089         if(this.placetitle.length){
31090             
31091             switch (this.placetitle) {
31092                 case 'center' :
31093                     cls += ' masonry-center-title';
31094                     break;
31095                 case 'bottom' :
31096                     cls += ' masonry-bottom-title';
31097                     break;
31098                 default:
31099                     break;
31100             }
31101             
31102         } else {
31103             if(!this.html.length && !this.bgimage.length){
31104                 cls += ' masonry-center-title';
31105             }
31106
31107             if(!this.html.length && this.bgimage.length){
31108                 cls += ' masonry-bottom-title';
31109             }
31110         }
31111         
31112         if(this.cls){
31113             cls += ' ' + this.cls;
31114         }
31115         
31116         var cfg = {
31117             tag: (this.href.length) ? 'a' : 'div',
31118             cls: cls,
31119             cn: [
31120                 {
31121                     tag: 'div',
31122                     cls: 'masonry-brick-paragraph',
31123                     cn: []
31124                 }
31125             ]
31126         };
31127         
31128         if(this.href.length){
31129             cfg.href = this.href;
31130         }
31131         
31132         var cn = cfg.cn[0].cn;
31133         
31134         if(this.title.length){
31135             cn.push({
31136                 tag: 'h4',
31137                 cls: 'masonry-brick-title',
31138                 html: this.title
31139             });
31140         }
31141         
31142         if(this.html.length){
31143             cn.push({
31144                 tag: 'p',
31145                 cls: 'masonry-brick-text',
31146                 html: this.html
31147             });
31148         }  
31149         if (!this.title.length && !this.html.length) {
31150             cfg.cn[0].cls += ' hide';
31151         }
31152         
31153         if(this.bgimage.length){
31154             cfg.cn.push({
31155                 tag: 'img',
31156                 cls: 'masonry-brick-image-view',
31157                 src: this.bgimage
31158             });
31159         }
31160         
31161         if(this.videourl.length){
31162             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31163             // youtube support only?
31164             cfg.cn.push({
31165                 tag: 'iframe',
31166                 cls: 'masonry-brick-image-view',
31167                 src: vurl,
31168                 frameborder : 0,
31169                 allowfullscreen : true
31170             });
31171             
31172             
31173         }
31174         
31175         cfg.cn.push({
31176             tag: 'div',
31177             cls: 'masonry-brick-mask'
31178         });
31179         
31180         return cfg;
31181         
31182     },
31183     
31184     getSplitAutoCreate : function()
31185     {
31186         var cls = 'masonry-brick masonry-brick-split';
31187         
31188         if(this.href.length){
31189             cls += ' masonry-brick-link';
31190         }
31191         
31192         if(this.bgimage.length){
31193             cls += ' masonry-brick-image';
31194         }
31195         
31196         if(this.size){
31197             cls += ' masonry-' + this.size + '-brick';
31198         }
31199         
31200         switch (this.placetitle) {
31201             case 'center' :
31202                 cls += ' masonry-center-title';
31203                 break;
31204             case 'bottom' :
31205                 cls += ' masonry-bottom-title';
31206                 break;
31207             default:
31208                 if(!this.bgimage.length){
31209                     cls += ' masonry-center-title';
31210                 }
31211
31212                 if(this.bgimage.length){
31213                     cls += ' masonry-bottom-title';
31214                 }
31215                 break;
31216         }
31217         
31218         if(this.cls){
31219             cls += ' ' + this.cls;
31220         }
31221         
31222         var cfg = {
31223             tag: (this.href.length) ? 'a' : 'div',
31224             cls: cls,
31225             cn: [
31226                 {
31227                     tag: 'div',
31228                     cls: 'masonry-brick-split-head',
31229                     cn: [
31230                         {
31231                             tag: 'div',
31232                             cls: 'masonry-brick-paragraph',
31233                             cn: []
31234                         }
31235                     ]
31236                 },
31237                 {
31238                     tag: 'div',
31239                     cls: 'masonry-brick-split-body',
31240                     cn: []
31241                 }
31242             ]
31243         };
31244         
31245         if(this.href.length){
31246             cfg.href = this.href;
31247         }
31248         
31249         if(this.title.length){
31250             cfg.cn[0].cn[0].cn.push({
31251                 tag: 'h4',
31252                 cls: 'masonry-brick-title',
31253                 html: this.title
31254             });
31255         }
31256         
31257         if(this.html.length){
31258             cfg.cn[1].cn.push({
31259                 tag: 'p',
31260                 cls: 'masonry-brick-text',
31261                 html: this.html
31262             });
31263         }
31264
31265         if(this.bgimage.length){
31266             cfg.cn[0].cn.push({
31267                 tag: 'img',
31268                 cls: 'masonry-brick-image-view',
31269                 src: this.bgimage
31270             });
31271         }
31272         
31273         if(this.videourl.length){
31274             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31275             // youtube support only?
31276             cfg.cn[0].cn.cn.push({
31277                 tag: 'iframe',
31278                 cls: 'masonry-brick-image-view',
31279                 src: vurl,
31280                 frameborder : 0,
31281                 allowfullscreen : true
31282             });
31283         }
31284         
31285         return cfg;
31286     },
31287     
31288     initEvents: function() 
31289     {
31290         switch (this.size) {
31291             case 'xs' :
31292                 this.x = 1;
31293                 this.y = 1;
31294                 break;
31295             case 'sm' :
31296                 this.x = 2;
31297                 this.y = 2;
31298                 break;
31299             case 'md' :
31300             case 'md-left' :
31301             case 'md-right' :
31302                 this.x = 3;
31303                 this.y = 3;
31304                 break;
31305             case 'tall' :
31306                 this.x = 2;
31307                 this.y = 3;
31308                 break;
31309             case 'wide' :
31310                 this.x = 3;
31311                 this.y = 2;
31312                 break;
31313             case 'wide-thin' :
31314                 this.x = 3;
31315                 this.y = 1;
31316                 break;
31317                         
31318             default :
31319                 break;
31320         }
31321         
31322         if(Roo.isTouch){
31323             this.el.on('touchstart', this.onTouchStart, this);
31324             this.el.on('touchmove', this.onTouchMove, this);
31325             this.el.on('touchend', this.onTouchEnd, this);
31326             this.el.on('contextmenu', this.onContextMenu, this);
31327         } else {
31328             this.el.on('mouseenter'  ,this.enter, this);
31329             this.el.on('mouseleave', this.leave, this);
31330             this.el.on('click', this.onClick, this);
31331         }
31332         
31333         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
31334             this.parent().bricks.push(this);   
31335         }
31336         
31337     },
31338     
31339     onClick: function(e, el)
31340     {
31341         var time = this.endTimer - this.startTimer;
31342         
31343         if(Roo.isTouch){
31344             if(time > 1000){
31345                 e.preventDefault();
31346                 return;
31347             }
31348         }
31349         
31350         if(!this.preventDefault){
31351             return;
31352         }
31353         
31354         e.preventDefault();
31355         this.fireEvent('click', this);
31356     },
31357     
31358     enter: function(e, el)
31359     {
31360         e.preventDefault();
31361         
31362         if(!this.isFitContainer){
31363             return;
31364         }
31365         
31366         if(this.bgimage.length && this.html.length){
31367             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31368         }
31369     },
31370     
31371     leave: function(e, el)
31372     {
31373         e.preventDefault();
31374         
31375         if(!this.isFitContainer){
31376             return;
31377         }
31378         
31379         if(this.bgimage.length && this.html.length){
31380             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31381         }
31382     },
31383     
31384     onTouchStart: function(e, el)
31385     {
31386 //        e.preventDefault();
31387         
31388         this.touchmoved = false;
31389         
31390         if(!this.isFitContainer){
31391             return;
31392         }
31393         
31394         if(!this.bgimage.length || !this.html.length){
31395             return;
31396         }
31397         
31398         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31399         
31400         this.timer = new Date().getTime();
31401         
31402     },
31403     
31404     onTouchMove: function(e, el)
31405     {
31406         this.touchmoved = true;
31407     },
31408     
31409     onContextMenu : function(e,el)
31410     {
31411         e.preventDefault();
31412         e.stopPropagation();
31413         return false;
31414     },
31415     
31416     onTouchEnd: function(e, el)
31417     {
31418 //        e.preventDefault();
31419         
31420         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
31421         
31422             this.leave(e,el);
31423             
31424             return;
31425         }
31426         
31427         if(!this.bgimage.length || !this.html.length){
31428             
31429             if(this.href.length){
31430                 window.location.href = this.href;
31431             }
31432             
31433             return;
31434         }
31435         
31436         if(!this.isFitContainer){
31437             return;
31438         }
31439         
31440         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31441         
31442         window.location.href = this.href;
31443     }
31444     
31445 });
31446
31447  
31448
31449  /*
31450  * - LGPL
31451  *
31452  * element
31453  * 
31454  */
31455
31456 /**
31457  * @class Roo.bootstrap.Brick
31458  * @extends Roo.bootstrap.Component
31459  * Bootstrap Brick class
31460  * 
31461  * @constructor
31462  * Create a new Brick
31463  * @param {Object} config The config object
31464  */
31465
31466 Roo.bootstrap.Brick = function(config){
31467     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
31468     
31469     this.addEvents({
31470         // raw events
31471         /**
31472          * @event click
31473          * When a Brick is click
31474          * @param {Roo.bootstrap.Brick} this
31475          * @param {Roo.EventObject} e
31476          */
31477         "click" : true
31478     });
31479 };
31480
31481 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
31482     
31483     /**
31484      * @cfg {String} title
31485      */   
31486     title : '',
31487     /**
31488      * @cfg {String} html
31489      */   
31490     html : '',
31491     /**
31492      * @cfg {String} bgimage
31493      */   
31494     bgimage : '',
31495     /**
31496      * @cfg {String} cls
31497      */   
31498     cls : '',
31499     /**
31500      * @cfg {String} href
31501      */   
31502     href : '',
31503     /**
31504      * @cfg {String} video
31505      */   
31506     video : '',
31507     /**
31508      * @cfg {Boolean} square
31509      */   
31510     square : true,
31511     
31512     getAutoCreate : function()
31513     {
31514         var cls = 'roo-brick';
31515         
31516         if(this.href.length){
31517             cls += ' roo-brick-link';
31518         }
31519         
31520         if(this.bgimage.length){
31521             cls += ' roo-brick-image';
31522         }
31523         
31524         if(!this.html.length && !this.bgimage.length){
31525             cls += ' roo-brick-center-title';
31526         }
31527         
31528         if(!this.html.length && this.bgimage.length){
31529             cls += ' roo-brick-bottom-title';
31530         }
31531         
31532         if(this.cls){
31533             cls += ' ' + this.cls;
31534         }
31535         
31536         var cfg = {
31537             tag: (this.href.length) ? 'a' : 'div',
31538             cls: cls,
31539             cn: [
31540                 {
31541                     tag: 'div',
31542                     cls: 'roo-brick-paragraph',
31543                     cn: []
31544                 }
31545             ]
31546         };
31547         
31548         if(this.href.length){
31549             cfg.href = this.href;
31550         }
31551         
31552         var cn = cfg.cn[0].cn;
31553         
31554         if(this.title.length){
31555             cn.push({
31556                 tag: 'h4',
31557                 cls: 'roo-brick-title',
31558                 html: this.title
31559             });
31560         }
31561         
31562         if(this.html.length){
31563             cn.push({
31564                 tag: 'p',
31565                 cls: 'roo-brick-text',
31566                 html: this.html
31567             });
31568         } else {
31569             cn.cls += ' hide';
31570         }
31571         
31572         if(this.bgimage.length){
31573             cfg.cn.push({
31574                 tag: 'img',
31575                 cls: 'roo-brick-image-view',
31576                 src: this.bgimage
31577             });
31578         }
31579         
31580         return cfg;
31581     },
31582     
31583     initEvents: function() 
31584     {
31585         if(this.title.length || this.html.length){
31586             this.el.on('mouseenter'  ,this.enter, this);
31587             this.el.on('mouseleave', this.leave, this);
31588         }
31589         
31590         
31591         Roo.EventManager.onWindowResize(this.resize, this); 
31592         
31593         this.resize();
31594     },
31595     
31596     resize : function()
31597     {
31598         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
31599         
31600         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
31601         
31602         if(this.bgimage.length){
31603             var image = this.el.select('.roo-brick-image-view', true).first();
31604             image.setWidth(paragraph.getWidth());
31605             image.setHeight(paragraph.getWidth());
31606             
31607             this.el.setHeight(paragraph.getWidth());
31608             
31609         }
31610         
31611     },
31612     
31613     enter: function(e, el)
31614     {
31615         e.preventDefault();
31616         
31617         if(this.bgimage.length){
31618             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
31619             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
31620         }
31621     },
31622     
31623     leave: function(e, el)
31624     {
31625         e.preventDefault();
31626         
31627         if(this.bgimage.length){
31628             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
31629             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
31630         }
31631     }
31632     
31633 });
31634
31635  
31636
31637  /*
31638  * - LGPL
31639  *
31640  * Input
31641  * 
31642  */
31643
31644 /**
31645  * @class Roo.bootstrap.NumberField
31646  * @extends Roo.bootstrap.Input
31647  * Bootstrap NumberField class
31648  * 
31649  * 
31650  * 
31651  * 
31652  * @constructor
31653  * Create a new NumberField
31654  * @param {Object} config The config object
31655  */
31656
31657 Roo.bootstrap.NumberField = function(config){
31658     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
31659 };
31660
31661 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
31662     
31663     /**
31664      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
31665      */
31666     allowDecimals : true,
31667     /**
31668      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
31669      */
31670     decimalSeparator : ".",
31671     /**
31672      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
31673      */
31674     decimalPrecision : 2,
31675     /**
31676      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
31677      */
31678     allowNegative : true,
31679     /**
31680      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
31681      */
31682     minValue : Number.NEGATIVE_INFINITY,
31683     /**
31684      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
31685      */
31686     maxValue : Number.MAX_VALUE,
31687     /**
31688      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
31689      */
31690     minText : "The minimum value for this field is {0}",
31691     /**
31692      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
31693      */
31694     maxText : "The maximum value for this field is {0}",
31695     /**
31696      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
31697      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
31698      */
31699     nanText : "{0} is not a valid number",
31700     /**
31701      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
31702      */
31703     castInt : true,
31704
31705     // private
31706     initEvents : function()
31707     {   
31708         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
31709         
31710         var allowed = "0123456789";
31711         
31712         if(this.allowDecimals){
31713             allowed += this.decimalSeparator;
31714         }
31715         
31716         if(this.allowNegative){
31717             allowed += "-";
31718         }
31719         
31720         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
31721         
31722         var keyPress = function(e){
31723             
31724             var k = e.getKey();
31725             
31726             var c = e.getCharCode();
31727             
31728             if(k == e.MINUS && allowed.indexOf(String.fromCharCode(c)) === -1){ // coz minus is mask as specialKey...
31729                 e.stopEvent();
31730                 return;
31731             }
31732             
31733             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
31734                 return;
31735             }
31736             
31737             if(allowed.indexOf(String.fromCharCode(c)) === -1){
31738                 e.stopEvent();
31739             }
31740         };
31741         
31742         this.el.on("keypress", keyPress, this);
31743     },
31744     
31745     validateValue : function(value)
31746     {
31747         
31748         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
31749             return false;
31750         }
31751         
31752         var num = this.parseValue(value);
31753         
31754         if(isNaN(num)){
31755             this.markInvalid(String.format(this.nanText, value));
31756             return false;
31757         }
31758         
31759         if(num < this.minValue){
31760             this.markInvalid(String.format(this.minText, this.minValue));
31761             return false;
31762         }
31763         
31764         if(num > this.maxValue){
31765             this.markInvalid(String.format(this.maxText, this.maxValue));
31766             return false;
31767         }
31768         
31769         return true;
31770     },
31771
31772     getValue : function()
31773     {
31774         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
31775     },
31776
31777     parseValue : function(value)
31778     {
31779         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
31780         return isNaN(value) ? '' : value;
31781     },
31782
31783     fixPrecision : function(value)
31784     {
31785         var nan = isNaN(value);
31786         
31787         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
31788             return nan ? '' : value;
31789         }
31790         return parseFloat(value).toFixed(this.decimalPrecision);
31791     },
31792
31793     setValue : function(v)
31794     {
31795         v = this.fixPrecision(v);
31796         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
31797     },
31798
31799     decimalPrecisionFcn : function(v)
31800     {
31801         return Math.floor(v);
31802     },
31803
31804     beforeBlur : function()
31805     {
31806         if(!this.castInt){
31807             return;
31808         }
31809         
31810         var v = this.parseValue(this.getRawValue());
31811         if(v){
31812             this.setValue(v);
31813         }
31814     }
31815     
31816 });
31817
31818  
31819 /*
31820  * Based on:
31821  * Ext JS Library 1.1.1
31822  * Copyright(c) 2006-2007, Ext JS, LLC.
31823  *
31824  * Originally Released Under LGPL - original licence link has changed is not relivant.
31825  *
31826  * Fork - LGPL
31827  * <script type="text/javascript">
31828  */
31829
31830
31831 /**
31832  * @class Roo.bootstrap.SplitBar
31833  * @extends Roo.util.Observable
31834  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
31835  * <br><br>
31836  * Usage:
31837  * <pre><code>
31838 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
31839                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
31840 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
31841 split.minSize = 100;
31842 split.maxSize = 600;
31843 split.animate = true;
31844 split.on('moved', splitterMoved);
31845 </code></pre>
31846  * @constructor
31847  * Create a new SplitBar
31848  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
31849  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
31850  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31851  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
31852                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
31853                         position of the SplitBar).
31854  */
31855 Roo.bootstrap.SplitBar = function(cfg){
31856     
31857     /** @private */
31858     
31859     //{
31860     //  dragElement : elm
31861     //  resizingElement: el,
31862         // optional..
31863     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
31864     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
31865         // existingProxy ???
31866     //}
31867     
31868     this.el = Roo.get(cfg.dragElement, true);
31869     this.el.dom.unselectable = "on";
31870     /** @private */
31871     this.resizingEl = Roo.get(cfg.resizingElement, true);
31872
31873     /**
31874      * @private
31875      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31876      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
31877      * @type Number
31878      */
31879     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
31880     
31881     /**
31882      * The minimum size of the resizing element. (Defaults to 0)
31883      * @type Number
31884      */
31885     this.minSize = 0;
31886     
31887     /**
31888      * The maximum size of the resizing element. (Defaults to 2000)
31889      * @type Number
31890      */
31891     this.maxSize = 2000;
31892     
31893     /**
31894      * Whether to animate the transition to the new size
31895      * @type Boolean
31896      */
31897     this.animate = false;
31898     
31899     /**
31900      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
31901      * @type Boolean
31902      */
31903     this.useShim = false;
31904     
31905     /** @private */
31906     this.shim = null;
31907     
31908     if(!cfg.existingProxy){
31909         /** @private */
31910         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
31911     }else{
31912         this.proxy = Roo.get(cfg.existingProxy).dom;
31913     }
31914     /** @private */
31915     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
31916     
31917     /** @private */
31918     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
31919     
31920     /** @private */
31921     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
31922     
31923     /** @private */
31924     this.dragSpecs = {};
31925     
31926     /**
31927      * @private The adapter to use to positon and resize elements
31928      */
31929     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31930     this.adapter.init(this);
31931     
31932     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31933         /** @private */
31934         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
31935         this.el.addClass("roo-splitbar-h");
31936     }else{
31937         /** @private */
31938         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
31939         this.el.addClass("roo-splitbar-v");
31940     }
31941     
31942     this.addEvents({
31943         /**
31944          * @event resize
31945          * Fires when the splitter is moved (alias for {@link #event-moved})
31946          * @param {Roo.bootstrap.SplitBar} this
31947          * @param {Number} newSize the new width or height
31948          */
31949         "resize" : true,
31950         /**
31951          * @event moved
31952          * Fires when the splitter is moved
31953          * @param {Roo.bootstrap.SplitBar} this
31954          * @param {Number} newSize the new width or height
31955          */
31956         "moved" : true,
31957         /**
31958          * @event beforeresize
31959          * Fires before the splitter is dragged
31960          * @param {Roo.bootstrap.SplitBar} this
31961          */
31962         "beforeresize" : true,
31963
31964         "beforeapply" : true
31965     });
31966
31967     Roo.util.Observable.call(this);
31968 };
31969
31970 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
31971     onStartProxyDrag : function(x, y){
31972         this.fireEvent("beforeresize", this);
31973         if(!this.overlay){
31974             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
31975             o.unselectable();
31976             o.enableDisplayMode("block");
31977             // all splitbars share the same overlay
31978             Roo.bootstrap.SplitBar.prototype.overlay = o;
31979         }
31980         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31981         this.overlay.show();
31982         Roo.get(this.proxy).setDisplayed("block");
31983         var size = this.adapter.getElementSize(this);
31984         this.activeMinSize = this.getMinimumSize();;
31985         this.activeMaxSize = this.getMaximumSize();;
31986         var c1 = size - this.activeMinSize;
31987         var c2 = Math.max(this.activeMaxSize - size, 0);
31988         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31989             this.dd.resetConstraints();
31990             this.dd.setXConstraint(
31991                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
31992                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
31993             );
31994             this.dd.setYConstraint(0, 0);
31995         }else{
31996             this.dd.resetConstraints();
31997             this.dd.setXConstraint(0, 0);
31998             this.dd.setYConstraint(
31999                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
32000                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
32001             );
32002          }
32003         this.dragSpecs.startSize = size;
32004         this.dragSpecs.startPoint = [x, y];
32005         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
32006     },
32007     
32008     /** 
32009      * @private Called after the drag operation by the DDProxy
32010      */
32011     onEndProxyDrag : function(e){
32012         Roo.get(this.proxy).setDisplayed(false);
32013         var endPoint = Roo.lib.Event.getXY(e);
32014         if(this.overlay){
32015             this.overlay.hide();
32016         }
32017         var newSize;
32018         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32019             newSize = this.dragSpecs.startSize + 
32020                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
32021                     endPoint[0] - this.dragSpecs.startPoint[0] :
32022                     this.dragSpecs.startPoint[0] - endPoint[0]
32023                 );
32024         }else{
32025             newSize = this.dragSpecs.startSize + 
32026                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
32027                     endPoint[1] - this.dragSpecs.startPoint[1] :
32028                     this.dragSpecs.startPoint[1] - endPoint[1]
32029                 );
32030         }
32031         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
32032         if(newSize != this.dragSpecs.startSize){
32033             if(this.fireEvent('beforeapply', this, newSize) !== false){
32034                 this.adapter.setElementSize(this, newSize);
32035                 this.fireEvent("moved", this, newSize);
32036                 this.fireEvent("resize", this, newSize);
32037             }
32038         }
32039     },
32040     
32041     /**
32042      * Get the adapter this SplitBar uses
32043      * @return The adapter object
32044      */
32045     getAdapter : function(){
32046         return this.adapter;
32047     },
32048     
32049     /**
32050      * Set the adapter this SplitBar uses
32051      * @param {Object} adapter A SplitBar adapter object
32052      */
32053     setAdapter : function(adapter){
32054         this.adapter = adapter;
32055         this.adapter.init(this);
32056     },
32057     
32058     /**
32059      * Gets the minimum size for the resizing element
32060      * @return {Number} The minimum size
32061      */
32062     getMinimumSize : function(){
32063         return this.minSize;
32064     },
32065     
32066     /**
32067      * Sets the minimum size for the resizing element
32068      * @param {Number} minSize The minimum size
32069      */
32070     setMinimumSize : function(minSize){
32071         this.minSize = minSize;
32072     },
32073     
32074     /**
32075      * Gets the maximum size for the resizing element
32076      * @return {Number} The maximum size
32077      */
32078     getMaximumSize : function(){
32079         return this.maxSize;
32080     },
32081     
32082     /**
32083      * Sets the maximum size for the resizing element
32084      * @param {Number} maxSize The maximum size
32085      */
32086     setMaximumSize : function(maxSize){
32087         this.maxSize = maxSize;
32088     },
32089     
32090     /**
32091      * Sets the initialize size for the resizing element
32092      * @param {Number} size The initial size
32093      */
32094     setCurrentSize : function(size){
32095         var oldAnimate = this.animate;
32096         this.animate = false;
32097         this.adapter.setElementSize(this, size);
32098         this.animate = oldAnimate;
32099     },
32100     
32101     /**
32102      * Destroy this splitbar. 
32103      * @param {Boolean} removeEl True to remove the element
32104      */
32105     destroy : function(removeEl){
32106         if(this.shim){
32107             this.shim.remove();
32108         }
32109         this.dd.unreg();
32110         this.proxy.parentNode.removeChild(this.proxy);
32111         if(removeEl){
32112             this.el.remove();
32113         }
32114     }
32115 });
32116
32117 /**
32118  * @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.
32119  */
32120 Roo.bootstrap.SplitBar.createProxy = function(dir){
32121     var proxy = new Roo.Element(document.createElement("div"));
32122     proxy.unselectable();
32123     var cls = 'roo-splitbar-proxy';
32124     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
32125     document.body.appendChild(proxy.dom);
32126     return proxy.dom;
32127 };
32128
32129 /** 
32130  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
32131  * Default Adapter. It assumes the splitter and resizing element are not positioned
32132  * elements and only gets/sets the width of the element. Generally used for table based layouts.
32133  */
32134 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
32135 };
32136
32137 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
32138     // do nothing for now
32139     init : function(s){
32140     
32141     },
32142     /**
32143      * Called before drag operations to get the current size of the resizing element. 
32144      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32145      */
32146      getElementSize : function(s){
32147         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32148             return s.resizingEl.getWidth();
32149         }else{
32150             return s.resizingEl.getHeight();
32151         }
32152     },
32153     
32154     /**
32155      * Called after drag operations to set the size of the resizing element.
32156      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32157      * @param {Number} newSize The new size to set
32158      * @param {Function} onComplete A function to be invoked when resizing is complete
32159      */
32160     setElementSize : function(s, newSize, onComplete){
32161         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32162             if(!s.animate){
32163                 s.resizingEl.setWidth(newSize);
32164                 if(onComplete){
32165                     onComplete(s, newSize);
32166                 }
32167             }else{
32168                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
32169             }
32170         }else{
32171             
32172             if(!s.animate){
32173                 s.resizingEl.setHeight(newSize);
32174                 if(onComplete){
32175                     onComplete(s, newSize);
32176                 }
32177             }else{
32178                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
32179             }
32180         }
32181     }
32182 };
32183
32184 /** 
32185  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
32186  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
32187  * Adapter that  moves the splitter element to align with the resized sizing element. 
32188  * Used with an absolute positioned SplitBar.
32189  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
32190  * document.body, make sure you assign an id to the body element.
32191  */
32192 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
32193     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32194     this.container = Roo.get(container);
32195 };
32196
32197 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
32198     init : function(s){
32199         this.basic.init(s);
32200     },
32201     
32202     getElementSize : function(s){
32203         return this.basic.getElementSize(s);
32204     },
32205     
32206     setElementSize : function(s, newSize, onComplete){
32207         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
32208     },
32209     
32210     moveSplitter : function(s){
32211         var yes = Roo.bootstrap.SplitBar;
32212         switch(s.placement){
32213             case yes.LEFT:
32214                 s.el.setX(s.resizingEl.getRight());
32215                 break;
32216             case yes.RIGHT:
32217                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
32218                 break;
32219             case yes.TOP:
32220                 s.el.setY(s.resizingEl.getBottom());
32221                 break;
32222             case yes.BOTTOM:
32223                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
32224                 break;
32225         }
32226     }
32227 };
32228
32229 /**
32230  * Orientation constant - Create a vertical SplitBar
32231  * @static
32232  * @type Number
32233  */
32234 Roo.bootstrap.SplitBar.VERTICAL = 1;
32235
32236 /**
32237  * Orientation constant - Create a horizontal SplitBar
32238  * @static
32239  * @type Number
32240  */
32241 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
32242
32243 /**
32244  * Placement constant - The resizing element is to the left of the splitter element
32245  * @static
32246  * @type Number
32247  */
32248 Roo.bootstrap.SplitBar.LEFT = 1;
32249
32250 /**
32251  * Placement constant - The resizing element is to the right of the splitter element
32252  * @static
32253  * @type Number
32254  */
32255 Roo.bootstrap.SplitBar.RIGHT = 2;
32256
32257 /**
32258  * Placement constant - The resizing element is positioned above the splitter element
32259  * @static
32260  * @type Number
32261  */
32262 Roo.bootstrap.SplitBar.TOP = 3;
32263
32264 /**
32265  * Placement constant - The resizing element is positioned under splitter element
32266  * @static
32267  * @type Number
32268  */
32269 Roo.bootstrap.SplitBar.BOTTOM = 4;
32270 Roo.namespace("Roo.bootstrap.layout");/*
32271  * Based on:
32272  * Ext JS Library 1.1.1
32273  * Copyright(c) 2006-2007, Ext JS, LLC.
32274  *
32275  * Originally Released Under LGPL - original licence link has changed is not relivant.
32276  *
32277  * Fork - LGPL
32278  * <script type="text/javascript">
32279  */
32280
32281 /**
32282  * @class Roo.bootstrap.layout.Manager
32283  * @extends Roo.bootstrap.Component
32284  * Base class for layout managers.
32285  */
32286 Roo.bootstrap.layout.Manager = function(config)
32287 {
32288     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
32289
32290
32291
32292
32293
32294     /** false to disable window resize monitoring @type Boolean */
32295     this.monitorWindowResize = true;
32296     this.regions = {};
32297     this.addEvents({
32298         /**
32299          * @event layout
32300          * Fires when a layout is performed.
32301          * @param {Roo.LayoutManager} this
32302          */
32303         "layout" : true,
32304         /**
32305          * @event regionresized
32306          * Fires when the user resizes a region.
32307          * @param {Roo.LayoutRegion} region The resized region
32308          * @param {Number} newSize The new size (width for east/west, height for north/south)
32309          */
32310         "regionresized" : true,
32311         /**
32312          * @event regioncollapsed
32313          * Fires when a region is collapsed.
32314          * @param {Roo.LayoutRegion} region The collapsed region
32315          */
32316         "regioncollapsed" : true,
32317         /**
32318          * @event regionexpanded
32319          * Fires when a region is expanded.
32320          * @param {Roo.LayoutRegion} region The expanded region
32321          */
32322         "regionexpanded" : true
32323     });
32324     this.updating = false;
32325
32326     if (config.el) {
32327         this.el = Roo.get(config.el);
32328         this.initEvents();
32329     }
32330
32331 };
32332
32333 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
32334
32335
32336     regions : null,
32337
32338     monitorWindowResize : true,
32339
32340
32341     updating : false,
32342
32343
32344     onRender : function(ct, position)
32345     {
32346         if(!this.el){
32347             this.el = Roo.get(ct);
32348             this.initEvents();
32349         }
32350         //this.fireEvent('render',this);
32351     },
32352
32353
32354     initEvents: function()
32355     {
32356
32357
32358         // ie scrollbar fix
32359         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32360             document.body.scroll = "no";
32361         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32362             this.el.position('relative');
32363         }
32364         this.id = this.el.id;
32365         this.el.addClass("roo-layout-container");
32366         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32367         if(this.el.dom != document.body ) {
32368             this.el.on('resize', this.layout,this);
32369             this.el.on('show', this.layout,this);
32370         }
32371
32372     },
32373
32374     /**
32375      * Returns true if this layout is currently being updated
32376      * @return {Boolean}
32377      */
32378     isUpdating : function(){
32379         return this.updating;
32380     },
32381
32382     /**
32383      * Suspend the LayoutManager from doing auto-layouts while
32384      * making multiple add or remove calls
32385      */
32386     beginUpdate : function(){
32387         this.updating = true;
32388     },
32389
32390     /**
32391      * Restore auto-layouts and optionally disable the manager from performing a layout
32392      * @param {Boolean} noLayout true to disable a layout update
32393      */
32394     endUpdate : function(noLayout){
32395         this.updating = false;
32396         if(!noLayout){
32397             this.layout();
32398         }
32399     },
32400
32401     layout: function(){
32402         // abstract...
32403     },
32404
32405     onRegionResized : function(region, newSize){
32406         this.fireEvent("regionresized", region, newSize);
32407         this.layout();
32408     },
32409
32410     onRegionCollapsed : function(region){
32411         this.fireEvent("regioncollapsed", region);
32412     },
32413
32414     onRegionExpanded : function(region){
32415         this.fireEvent("regionexpanded", region);
32416     },
32417
32418     /**
32419      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32420      * performs box-model adjustments.
32421      * @return {Object} The size as an object {width: (the width), height: (the height)}
32422      */
32423     getViewSize : function()
32424     {
32425         var size;
32426         if(this.el.dom != document.body){
32427             size = this.el.getSize();
32428         }else{
32429             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32430         }
32431         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32432         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32433         return size;
32434     },
32435
32436     /**
32437      * Returns the Element this layout is bound to.
32438      * @return {Roo.Element}
32439      */
32440     getEl : function(){
32441         return this.el;
32442     },
32443
32444     /**
32445      * Returns the specified region.
32446      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32447      * @return {Roo.LayoutRegion}
32448      */
32449     getRegion : function(target){
32450         return this.regions[target.toLowerCase()];
32451     },
32452
32453     onWindowResize : function(){
32454         if(this.monitorWindowResize){
32455             this.layout();
32456         }
32457     }
32458 });
32459 /*
32460  * Based on:
32461  * Ext JS Library 1.1.1
32462  * Copyright(c) 2006-2007, Ext JS, LLC.
32463  *
32464  * Originally Released Under LGPL - original licence link has changed is not relivant.
32465  *
32466  * Fork - LGPL
32467  * <script type="text/javascript">
32468  */
32469 /**
32470  * @class Roo.bootstrap.layout.Border
32471  * @extends Roo.bootstrap.layout.Manager
32472  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32473  * please see: examples/bootstrap/nested.html<br><br>
32474  
32475 <b>The container the layout is rendered into can be either the body element or any other element.
32476 If it is not the body element, the container needs to either be an absolute positioned element,
32477 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32478 the container size if it is not the body element.</b>
32479
32480 * @constructor
32481 * Create a new Border
32482 * @param {Object} config Configuration options
32483  */
32484 Roo.bootstrap.layout.Border = function(config){
32485     config = config || {};
32486     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
32487     
32488     
32489     
32490     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32491         if(config[region]){
32492             config[region].region = region;
32493             this.addRegion(config[region]);
32494         }
32495     },this);
32496     
32497 };
32498
32499 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
32500
32501 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
32502     /**
32503      * Creates and adds a new region if it doesn't already exist.
32504      * @param {String} target The target region key (north, south, east, west or center).
32505      * @param {Object} config The regions config object
32506      * @return {BorderLayoutRegion} The new region
32507      */
32508     addRegion : function(config)
32509     {
32510         if(!this.regions[config.region]){
32511             var r = this.factory(config);
32512             this.bindRegion(r);
32513         }
32514         return this.regions[config.region];
32515     },
32516
32517     // private (kinda)
32518     bindRegion : function(r){
32519         this.regions[r.config.region] = r;
32520         
32521         r.on("visibilitychange",    this.layout, this);
32522         r.on("paneladded",          this.layout, this);
32523         r.on("panelremoved",        this.layout, this);
32524         r.on("invalidated",         this.layout, this);
32525         r.on("resized",             this.onRegionResized, this);
32526         r.on("collapsed",           this.onRegionCollapsed, this);
32527         r.on("expanded",            this.onRegionExpanded, this);
32528     },
32529
32530     /**
32531      * Performs a layout update.
32532      */
32533     layout : function()
32534     {
32535         if(this.updating) {
32536             return;
32537         }
32538         
32539         // render all the rebions if they have not been done alreayd?
32540         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32541             if(this.regions[region] && !this.regions[region].bodyEl){
32542                 this.regions[region].onRender(this.el)
32543             }
32544         },this);
32545         
32546         var size = this.getViewSize();
32547         var w = size.width;
32548         var h = size.height;
32549         var centerW = w;
32550         var centerH = h;
32551         var centerY = 0;
32552         var centerX = 0;
32553         //var x = 0, y = 0;
32554
32555         var rs = this.regions;
32556         var north = rs["north"];
32557         var south = rs["south"]; 
32558         var west = rs["west"];
32559         var east = rs["east"];
32560         var center = rs["center"];
32561         //if(this.hideOnLayout){ // not supported anymore
32562             //c.el.setStyle("display", "none");
32563         //}
32564         if(north && north.isVisible()){
32565             var b = north.getBox();
32566             var m = north.getMargins();
32567             b.width = w - (m.left+m.right);
32568             b.x = m.left;
32569             b.y = m.top;
32570             centerY = b.height + b.y + m.bottom;
32571             centerH -= centerY;
32572             north.updateBox(this.safeBox(b));
32573         }
32574         if(south && south.isVisible()){
32575             var b = south.getBox();
32576             var m = south.getMargins();
32577             b.width = w - (m.left+m.right);
32578             b.x = m.left;
32579             var totalHeight = (b.height + m.top + m.bottom);
32580             b.y = h - totalHeight + m.top;
32581             centerH -= totalHeight;
32582             south.updateBox(this.safeBox(b));
32583         }
32584         if(west && west.isVisible()){
32585             var b = west.getBox();
32586             var m = west.getMargins();
32587             b.height = centerH - (m.top+m.bottom);
32588             b.x = m.left;
32589             b.y = centerY + m.top;
32590             var totalWidth = (b.width + m.left + m.right);
32591             centerX += totalWidth;
32592             centerW -= totalWidth;
32593             west.updateBox(this.safeBox(b));
32594         }
32595         if(east && east.isVisible()){
32596             var b = east.getBox();
32597             var m = east.getMargins();
32598             b.height = centerH - (m.top+m.bottom);
32599             var totalWidth = (b.width + m.left + m.right);
32600             b.x = w - totalWidth + m.left;
32601             b.y = centerY + m.top;
32602             centerW -= totalWidth;
32603             east.updateBox(this.safeBox(b));
32604         }
32605         if(center){
32606             var m = center.getMargins();
32607             var centerBox = {
32608                 x: centerX + m.left,
32609                 y: centerY + m.top,
32610                 width: centerW - (m.left+m.right),
32611                 height: centerH - (m.top+m.bottom)
32612             };
32613             //if(this.hideOnLayout){
32614                 //center.el.setStyle("display", "block");
32615             //}
32616             center.updateBox(this.safeBox(centerBox));
32617         }
32618         this.el.repaint();
32619         this.fireEvent("layout", this);
32620     },
32621
32622     // private
32623     safeBox : function(box){
32624         box.width = Math.max(0, box.width);
32625         box.height = Math.max(0, box.height);
32626         return box;
32627     },
32628
32629     /**
32630      * Adds a ContentPanel (or subclass) to this layout.
32631      * @param {String} target The target region key (north, south, east, west or center).
32632      * @param {Roo.ContentPanel} panel The panel to add
32633      * @return {Roo.ContentPanel} The added panel
32634      */
32635     add : function(target, panel){
32636          
32637         target = target.toLowerCase();
32638         return this.regions[target].add(panel);
32639     },
32640
32641     /**
32642      * Remove a ContentPanel (or subclass) to this layout.
32643      * @param {String} target The target region key (north, south, east, west or center).
32644      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32645      * @return {Roo.ContentPanel} The removed panel
32646      */
32647     remove : function(target, panel){
32648         target = target.toLowerCase();
32649         return this.regions[target].remove(panel);
32650     },
32651
32652     /**
32653      * Searches all regions for a panel with the specified id
32654      * @param {String} panelId
32655      * @return {Roo.ContentPanel} The panel or null if it wasn't found
32656      */
32657     findPanel : function(panelId){
32658         var rs = this.regions;
32659         for(var target in rs){
32660             if(typeof rs[target] != "function"){
32661                 var p = rs[target].getPanel(panelId);
32662                 if(p){
32663                     return p;
32664                 }
32665             }
32666         }
32667         return null;
32668     },
32669
32670     /**
32671      * Searches all regions for a panel with the specified id and activates (shows) it.
32672      * @param {String/ContentPanel} panelId The panels id or the panel itself
32673      * @return {Roo.ContentPanel} The shown panel or null
32674      */
32675     showPanel : function(panelId) {
32676       var rs = this.regions;
32677       for(var target in rs){
32678          var r = rs[target];
32679          if(typeof r != "function"){
32680             if(r.hasPanel(panelId)){
32681                return r.showPanel(panelId);
32682             }
32683          }
32684       }
32685       return null;
32686    },
32687
32688    /**
32689      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32690      * @param {Roo.state.Provider} provider (optional) An alternate state provider
32691      */
32692    /*
32693     restoreState : function(provider){
32694         if(!provider){
32695             provider = Roo.state.Manager;
32696         }
32697         var sm = new Roo.LayoutStateManager();
32698         sm.init(this, provider);
32699     },
32700 */
32701  
32702  
32703     /**
32704      * Adds a xtype elements to the layout.
32705      * <pre><code>
32706
32707 layout.addxtype({
32708        xtype : 'ContentPanel',
32709        region: 'west',
32710        items: [ .... ]
32711    }
32712 );
32713
32714 layout.addxtype({
32715         xtype : 'NestedLayoutPanel',
32716         region: 'west',
32717         layout: {
32718            center: { },
32719            west: { }   
32720         },
32721         items : [ ... list of content panels or nested layout panels.. ]
32722    }
32723 );
32724 </code></pre>
32725      * @param {Object} cfg Xtype definition of item to add.
32726      */
32727     addxtype : function(cfg)
32728     {
32729         // basically accepts a pannel...
32730         // can accept a layout region..!?!?
32731         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32732         
32733         
32734         // theory?  children can only be panels??
32735         
32736         //if (!cfg.xtype.match(/Panel$/)) {
32737         //    return false;
32738         //}
32739         var ret = false;
32740         
32741         if (typeof(cfg.region) == 'undefined') {
32742             Roo.log("Failed to add Panel, region was not set");
32743             Roo.log(cfg);
32744             return false;
32745         }
32746         var region = cfg.region;
32747         delete cfg.region;
32748         
32749           
32750         var xitems = [];
32751         if (cfg.items) {
32752             xitems = cfg.items;
32753             delete cfg.items;
32754         }
32755         var nb = false;
32756         
32757         switch(cfg.xtype) 
32758         {
32759             case 'Content':  // ContentPanel (el, cfg)
32760             case 'Scroll':  // ContentPanel (el, cfg)
32761             case 'View': 
32762                 cfg.autoCreate = true;
32763                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32764                 //} else {
32765                 //    var el = this.el.createChild();
32766                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32767                 //}
32768                 
32769                 this.add(region, ret);
32770                 break;
32771             
32772             /*
32773             case 'TreePanel': // our new panel!
32774                 cfg.el = this.el.createChild();
32775                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32776                 this.add(region, ret);
32777                 break;
32778             */
32779             
32780             case 'Nest': 
32781                 // create a new Layout (which is  a Border Layout...
32782                 
32783                 var clayout = cfg.layout;
32784                 clayout.el  = this.el.createChild();
32785                 clayout.items   = clayout.items  || [];
32786                 
32787                 delete cfg.layout;
32788                 
32789                 // replace this exitems with the clayout ones..
32790                 xitems = clayout.items;
32791                  
32792                 // force background off if it's in center...
32793                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32794                     cfg.background = false;
32795                 }
32796                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
32797                 
32798                 
32799                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32800                 //console.log('adding nested layout panel '  + cfg.toSource());
32801                 this.add(region, ret);
32802                 nb = {}; /// find first...
32803                 break;
32804             
32805             case 'Grid':
32806                 
32807                 // needs grid and region
32808                 
32809                 //var el = this.getRegion(region).el.createChild();
32810                 /*
32811                  *var el = this.el.createChild();
32812                 // create the grid first...
32813                 cfg.grid.container = el;
32814                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
32815                 */
32816                 
32817                 if (region == 'center' && this.active ) {
32818                     cfg.background = false;
32819                 }
32820                 
32821                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32822                 
32823                 this.add(region, ret);
32824                 /*
32825                 if (cfg.background) {
32826                     // render grid on panel activation (if panel background)
32827                     ret.on('activate', function(gp) {
32828                         if (!gp.grid.rendered) {
32829                     //        gp.grid.render(el);
32830                         }
32831                     });
32832                 } else {
32833                   //  cfg.grid.render(el);
32834                 }
32835                 */
32836                 break;
32837            
32838            
32839             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
32840                 // it was the old xcomponent building that caused this before.
32841                 // espeically if border is the top element in the tree.
32842                 ret = this;
32843                 break; 
32844                 
32845                     
32846                 
32847                 
32848                 
32849             default:
32850                 /*
32851                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
32852                     
32853                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32854                     this.add(region, ret);
32855                 } else {
32856                 */
32857                     Roo.log(cfg);
32858                     throw "Can not add '" + cfg.xtype + "' to Border";
32859                     return null;
32860              
32861                                 
32862              
32863         }
32864         this.beginUpdate();
32865         // add children..
32866         var region = '';
32867         var abn = {};
32868         Roo.each(xitems, function(i)  {
32869             region = nb && i.region ? i.region : false;
32870             
32871             var add = ret.addxtype(i);
32872            
32873             if (region) {
32874                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32875                 if (!i.background) {
32876                     abn[region] = nb[region] ;
32877                 }
32878             }
32879             
32880         });
32881         this.endUpdate();
32882
32883         // make the last non-background panel active..
32884         //if (nb) { Roo.log(abn); }
32885         if (nb) {
32886             
32887             for(var r in abn) {
32888                 region = this.getRegion(r);
32889                 if (region) {
32890                     // tried using nb[r], but it does not work..
32891                      
32892                     region.showPanel(abn[r]);
32893                    
32894                 }
32895             }
32896         }
32897         return ret;
32898         
32899     },
32900     
32901     
32902 // private
32903     factory : function(cfg)
32904     {
32905         
32906         var validRegions = Roo.bootstrap.layout.Border.regions;
32907
32908         var target = cfg.region;
32909         cfg.mgr = this;
32910         
32911         var r = Roo.bootstrap.layout;
32912         Roo.log(target);
32913         switch(target){
32914             case "north":
32915                 return new r.North(cfg);
32916             case "south":
32917                 return new r.South(cfg);
32918             case "east":
32919                 return new r.East(cfg);
32920             case "west":
32921                 return new r.West(cfg);
32922             case "center":
32923                 return new r.Center(cfg);
32924         }
32925         throw 'Layout region "'+target+'" not supported.';
32926     }
32927     
32928     
32929 });
32930  /*
32931  * Based on:
32932  * Ext JS Library 1.1.1
32933  * Copyright(c) 2006-2007, Ext JS, LLC.
32934  *
32935  * Originally Released Under LGPL - original licence link has changed is not relivant.
32936  *
32937  * Fork - LGPL
32938  * <script type="text/javascript">
32939  */
32940  
32941 /**
32942  * @class Roo.bootstrap.layout.Basic
32943  * @extends Roo.util.Observable
32944  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32945  * and does not have a titlebar, tabs or any other features. All it does is size and position 
32946  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32947  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
32948  * @cfg {string}   region  the region that it inhabits..
32949  * @cfg {bool}   skipConfig skip config?
32950  * 
32951
32952  */
32953 Roo.bootstrap.layout.Basic = function(config){
32954     
32955     this.mgr = config.mgr;
32956     
32957     this.position = config.region;
32958     
32959     var skipConfig = config.skipConfig;
32960     
32961     this.events = {
32962         /**
32963          * @scope Roo.BasicLayoutRegion
32964          */
32965         
32966         /**
32967          * @event beforeremove
32968          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32969          * @param {Roo.LayoutRegion} this
32970          * @param {Roo.ContentPanel} panel The panel
32971          * @param {Object} e The cancel event object
32972          */
32973         "beforeremove" : true,
32974         /**
32975          * @event invalidated
32976          * Fires when the layout for this region is changed.
32977          * @param {Roo.LayoutRegion} this
32978          */
32979         "invalidated" : true,
32980         /**
32981          * @event visibilitychange
32982          * Fires when this region is shown or hidden 
32983          * @param {Roo.LayoutRegion} this
32984          * @param {Boolean} visibility true or false
32985          */
32986         "visibilitychange" : true,
32987         /**
32988          * @event paneladded
32989          * Fires when a panel is added. 
32990          * @param {Roo.LayoutRegion} this
32991          * @param {Roo.ContentPanel} panel The panel
32992          */
32993         "paneladded" : true,
32994         /**
32995          * @event panelremoved
32996          * Fires when a panel is removed. 
32997          * @param {Roo.LayoutRegion} this
32998          * @param {Roo.ContentPanel} panel The panel
32999          */
33000         "panelremoved" : true,
33001         /**
33002          * @event beforecollapse
33003          * Fires when this region before collapse.
33004          * @param {Roo.LayoutRegion} this
33005          */
33006         "beforecollapse" : true,
33007         /**
33008          * @event collapsed
33009          * Fires when this region is collapsed.
33010          * @param {Roo.LayoutRegion} this
33011          */
33012         "collapsed" : true,
33013         /**
33014          * @event expanded
33015          * Fires when this region is expanded.
33016          * @param {Roo.LayoutRegion} this
33017          */
33018         "expanded" : true,
33019         /**
33020          * @event slideshow
33021          * Fires when this region is slid into view.
33022          * @param {Roo.LayoutRegion} this
33023          */
33024         "slideshow" : true,
33025         /**
33026          * @event slidehide
33027          * Fires when this region slides out of view. 
33028          * @param {Roo.LayoutRegion} this
33029          */
33030         "slidehide" : true,
33031         /**
33032          * @event panelactivated
33033          * Fires when a panel is activated. 
33034          * @param {Roo.LayoutRegion} this
33035          * @param {Roo.ContentPanel} panel The activated panel
33036          */
33037         "panelactivated" : true,
33038         /**
33039          * @event resized
33040          * Fires when the user resizes this region. 
33041          * @param {Roo.LayoutRegion} this
33042          * @param {Number} newSize The new size (width for east/west, height for north/south)
33043          */
33044         "resized" : true
33045     };
33046     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33047     this.panels = new Roo.util.MixedCollection();
33048     this.panels.getKey = this.getPanelId.createDelegate(this);
33049     this.box = null;
33050     this.activePanel = null;
33051     // ensure listeners are added...
33052     
33053     if (config.listeners || config.events) {
33054         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
33055             listeners : config.listeners || {},
33056             events : config.events || {}
33057         });
33058     }
33059     
33060     if(skipConfig !== true){
33061         this.applyConfig(config);
33062     }
33063 };
33064
33065 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
33066 {
33067     getPanelId : function(p){
33068         return p.getId();
33069     },
33070     
33071     applyConfig : function(config){
33072         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33073         this.config = config;
33074         
33075     },
33076     
33077     /**
33078      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33079      * the width, for horizontal (north, south) the height.
33080      * @param {Number} newSize The new width or height
33081      */
33082     resizeTo : function(newSize){
33083         var el = this.el ? this.el :
33084                  (this.activePanel ? this.activePanel.getEl() : null);
33085         if(el){
33086             switch(this.position){
33087                 case "east":
33088                 case "west":
33089                     el.setWidth(newSize);
33090                     this.fireEvent("resized", this, newSize);
33091                 break;
33092                 case "north":
33093                 case "south":
33094                     el.setHeight(newSize);
33095                     this.fireEvent("resized", this, newSize);
33096                 break;                
33097             }
33098         }
33099     },
33100     
33101     getBox : function(){
33102         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33103     },
33104     
33105     getMargins : function(){
33106         return this.margins;
33107     },
33108     
33109     updateBox : function(box){
33110         this.box = box;
33111         var el = this.activePanel.getEl();
33112         el.dom.style.left = box.x + "px";
33113         el.dom.style.top = box.y + "px";
33114         this.activePanel.setSize(box.width, box.height);
33115     },
33116     
33117     /**
33118      * Returns the container element for this region.
33119      * @return {Roo.Element}
33120      */
33121     getEl : function(){
33122         return this.activePanel;
33123     },
33124     
33125     /**
33126      * Returns true if this region is currently visible.
33127      * @return {Boolean}
33128      */
33129     isVisible : function(){
33130         return this.activePanel ? true : false;
33131     },
33132     
33133     setActivePanel : function(panel){
33134         panel = this.getPanel(panel);
33135         if(this.activePanel && this.activePanel != panel){
33136             this.activePanel.setActiveState(false);
33137             this.activePanel.getEl().setLeftTop(-10000,-10000);
33138         }
33139         this.activePanel = panel;
33140         panel.setActiveState(true);
33141         if(this.box){
33142             panel.setSize(this.box.width, this.box.height);
33143         }
33144         this.fireEvent("panelactivated", this, panel);
33145         this.fireEvent("invalidated");
33146     },
33147     
33148     /**
33149      * Show the specified panel.
33150      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33151      * @return {Roo.ContentPanel} The shown panel or null
33152      */
33153     showPanel : function(panel){
33154         panel = this.getPanel(panel);
33155         if(panel){
33156             this.setActivePanel(panel);
33157         }
33158         return panel;
33159     },
33160     
33161     /**
33162      * Get the active panel for this region.
33163      * @return {Roo.ContentPanel} The active panel or null
33164      */
33165     getActivePanel : function(){
33166         return this.activePanel;
33167     },
33168     
33169     /**
33170      * Add the passed ContentPanel(s)
33171      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33172      * @return {Roo.ContentPanel} The panel added (if only one was added)
33173      */
33174     add : function(panel){
33175         if(arguments.length > 1){
33176             for(var i = 0, len = arguments.length; i < len; i++) {
33177                 this.add(arguments[i]);
33178             }
33179             return null;
33180         }
33181         if(this.hasPanel(panel)){
33182             this.showPanel(panel);
33183             return panel;
33184         }
33185         var el = panel.getEl();
33186         if(el.dom.parentNode != this.mgr.el.dom){
33187             this.mgr.el.dom.appendChild(el.dom);
33188         }
33189         if(panel.setRegion){
33190             panel.setRegion(this);
33191         }
33192         this.panels.add(panel);
33193         el.setStyle("position", "absolute");
33194         if(!panel.background){
33195             this.setActivePanel(panel);
33196             if(this.config.initialSize && this.panels.getCount()==1){
33197                 this.resizeTo(this.config.initialSize);
33198             }
33199         }
33200         this.fireEvent("paneladded", this, panel);
33201         return panel;
33202     },
33203     
33204     /**
33205      * Returns true if the panel is in this region.
33206      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33207      * @return {Boolean}
33208      */
33209     hasPanel : function(panel){
33210         if(typeof panel == "object"){ // must be panel obj
33211             panel = panel.getId();
33212         }
33213         return this.getPanel(panel) ? true : false;
33214     },
33215     
33216     /**
33217      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33218      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33219      * @param {Boolean} preservePanel Overrides the config preservePanel option
33220      * @return {Roo.ContentPanel} The panel that was removed
33221      */
33222     remove : function(panel, preservePanel){
33223         panel = this.getPanel(panel);
33224         if(!panel){
33225             return null;
33226         }
33227         var e = {};
33228         this.fireEvent("beforeremove", this, panel, e);
33229         if(e.cancel === true){
33230             return null;
33231         }
33232         var panelId = panel.getId();
33233         this.panels.removeKey(panelId);
33234         return panel;
33235     },
33236     
33237     /**
33238      * Returns the panel specified or null if it's not in this region.
33239      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33240      * @return {Roo.ContentPanel}
33241      */
33242     getPanel : function(id){
33243         if(typeof id == "object"){ // must be panel obj
33244             return id;
33245         }
33246         return this.panels.get(id);
33247     },
33248     
33249     /**
33250      * Returns this regions position (north/south/east/west/center).
33251      * @return {String} 
33252      */
33253     getPosition: function(){
33254         return this.position;    
33255     }
33256 });/*
33257  * Based on:
33258  * Ext JS Library 1.1.1
33259  * Copyright(c) 2006-2007, Ext JS, LLC.
33260  *
33261  * Originally Released Under LGPL - original licence link has changed is not relivant.
33262  *
33263  * Fork - LGPL
33264  * <script type="text/javascript">
33265  */
33266  
33267 /**
33268  * @class Roo.bootstrap.layout.Region
33269  * @extends Roo.bootstrap.layout.Basic
33270  * This class represents a region in a layout manager.
33271  
33272  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33273  * @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})
33274  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
33275  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33276  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33277  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33278  * @cfg {String}    title           The title for the region (overrides panel titles)
33279  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33280  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33281  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33282  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33283  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33284  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33285  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33286  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33287  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33288  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
33289
33290  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33291  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33292  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33293  * @cfg {Number}    width           For East/West panels
33294  * @cfg {Number}    height          For North/South panels
33295  * @cfg {Boolean}   split           To show the splitter
33296  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33297  * 
33298  * @cfg {string}   cls             Extra CSS classes to add to region
33299  * 
33300  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
33301  * @cfg {string}   region  the region that it inhabits..
33302  *
33303
33304  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
33305  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
33306
33307  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
33308  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
33309  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
33310  */
33311 Roo.bootstrap.layout.Region = function(config)
33312 {
33313     this.applyConfig(config);
33314
33315     var mgr = config.mgr;
33316     var pos = config.region;
33317     config.skipConfig = true;
33318     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
33319     
33320     if (mgr.el) {
33321         this.onRender(mgr.el);   
33322     }
33323      
33324     this.visible = true;
33325     this.collapsed = false;
33326     this.unrendered_panels = [];
33327 };
33328
33329 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
33330
33331     position: '', // set by wrapper (eg. north/south etc..)
33332     unrendered_panels : null,  // unrendered panels.
33333     createBody : function(){
33334         /** This region's body element 
33335         * @type Roo.Element */
33336         this.bodyEl = this.el.createChild({
33337                 tag: "div",
33338                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
33339         });
33340     },
33341
33342     onRender: function(ctr, pos)
33343     {
33344         var dh = Roo.DomHelper;
33345         /** This region's container element 
33346         * @type Roo.Element */
33347         this.el = dh.append(ctr.dom, {
33348                 tag: "div",
33349                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
33350             }, true);
33351         /** This region's title element 
33352         * @type Roo.Element */
33353     
33354         this.titleEl = dh.append(this.el.dom,
33355             {
33356                     tag: "div",
33357                     unselectable: "on",
33358                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
33359                     children:[
33360                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33361                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
33362                     ]}, true);
33363         
33364         this.titleEl.enableDisplayMode();
33365         /** This region's title text element 
33366         * @type HTMLElement */
33367         this.titleTextEl = this.titleEl.dom.firstChild;
33368         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33369         /*
33370         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
33371         this.closeBtn.enableDisplayMode();
33372         this.closeBtn.on("click", this.closeClicked, this);
33373         this.closeBtn.hide();
33374     */
33375         this.createBody(this.config);
33376         if(this.config.hideWhenEmpty){
33377             this.hide();
33378             this.on("paneladded", this.validateVisibility, this);
33379             this.on("panelremoved", this.validateVisibility, this);
33380         }
33381         if(this.autoScroll){
33382             this.bodyEl.setStyle("overflow", "auto");
33383         }else{
33384             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
33385         }
33386         //if(c.titlebar !== false){
33387             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
33388                 this.titleEl.hide();
33389             }else{
33390                 this.titleEl.show();
33391                 if(this.config.title){
33392                     this.titleTextEl.innerHTML = this.config.title;
33393                 }
33394             }
33395         //}
33396         if(this.config.collapsed){
33397             this.collapse(true);
33398         }
33399         if(this.config.hidden){
33400             this.hide();
33401         }
33402         
33403         if (this.unrendered_panels && this.unrendered_panels.length) {
33404             for (var i =0;i< this.unrendered_panels.length; i++) {
33405                 this.add(this.unrendered_panels[i]);
33406             }
33407             this.unrendered_panels = null;
33408             
33409         }
33410         
33411     },
33412     
33413     applyConfig : function(c)
33414     {
33415         /*
33416          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
33417             var dh = Roo.DomHelper;
33418             if(c.titlebar !== false){
33419                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
33420                 this.collapseBtn.on("click", this.collapse, this);
33421                 this.collapseBtn.enableDisplayMode();
33422                 /*
33423                 if(c.showPin === true || this.showPin){
33424                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
33425                     this.stickBtn.enableDisplayMode();
33426                     this.stickBtn.on("click", this.expand, this);
33427                     this.stickBtn.hide();
33428                 }
33429                 
33430             }
33431             */
33432             /** This region's collapsed element
33433             * @type Roo.Element */
33434             /*
33435              *
33436             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33437                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33438             ]}, true);
33439             
33440             if(c.floatable !== false){
33441                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33442                this.collapsedEl.on("click", this.collapseClick, this);
33443             }
33444
33445             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33446                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33447                    id: "message", unselectable: "on", style:{"float":"left"}});
33448                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33449              }
33450             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33451             this.expandBtn.on("click", this.expand, this);
33452             
33453         }
33454         
33455         if(this.collapseBtn){
33456             this.collapseBtn.setVisible(c.collapsible == true);
33457         }
33458         
33459         this.cmargins = c.cmargins || this.cmargins ||
33460                          (this.position == "west" || this.position == "east" ?
33461                              {top: 0, left: 2, right:2, bottom: 0} :
33462                              {top: 2, left: 0, right:0, bottom: 2});
33463         */
33464         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33465         
33466         
33467         this.bottomTabs = c.tabPosition != "top";
33468         
33469         this.autoScroll = c.autoScroll || false;
33470         
33471         
33472        
33473         
33474         this.duration = c.duration || .30;
33475         this.slideDuration = c.slideDuration || .45;
33476         this.config = c;
33477        
33478     },
33479     /**
33480      * Returns true if this region is currently visible.
33481      * @return {Boolean}
33482      */
33483     isVisible : function(){
33484         return this.visible;
33485     },
33486
33487     /**
33488      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33489      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33490      */
33491     //setCollapsedTitle : function(title){
33492     //    title = title || "&#160;";
33493      //   if(this.collapsedTitleTextEl){
33494       //      this.collapsedTitleTextEl.innerHTML = title;
33495        // }
33496     //},
33497
33498     getBox : function(){
33499         var b;
33500       //  if(!this.collapsed){
33501             b = this.el.getBox(false, true);
33502        // }else{
33503           //  b = this.collapsedEl.getBox(false, true);
33504         //}
33505         return b;
33506     },
33507
33508     getMargins : function(){
33509         return this.margins;
33510         //return this.collapsed ? this.cmargins : this.margins;
33511     },
33512 /*
33513     highlight : function(){
33514         this.el.addClass("x-layout-panel-dragover");
33515     },
33516
33517     unhighlight : function(){
33518         this.el.removeClass("x-layout-panel-dragover");
33519     },
33520 */
33521     updateBox : function(box)
33522     {
33523         if (!this.bodyEl) {
33524             return; // not rendered yet..
33525         }
33526         
33527         this.box = box;
33528         if(!this.collapsed){
33529             this.el.dom.style.left = box.x + "px";
33530             this.el.dom.style.top = box.y + "px";
33531             this.updateBody(box.width, box.height);
33532         }else{
33533             this.collapsedEl.dom.style.left = box.x + "px";
33534             this.collapsedEl.dom.style.top = box.y + "px";
33535             this.collapsedEl.setSize(box.width, box.height);
33536         }
33537         if(this.tabs){
33538             this.tabs.autoSizeTabs();
33539         }
33540     },
33541
33542     updateBody : function(w, h)
33543     {
33544         if(w !== null){
33545             this.el.setWidth(w);
33546             w -= this.el.getBorderWidth("rl");
33547             if(this.config.adjustments){
33548                 w += this.config.adjustments[0];
33549             }
33550         }
33551         if(h !== null && h > 0){
33552             this.el.setHeight(h);
33553             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33554             h -= this.el.getBorderWidth("tb");
33555             if(this.config.adjustments){
33556                 h += this.config.adjustments[1];
33557             }
33558             this.bodyEl.setHeight(h);
33559             if(this.tabs){
33560                 h = this.tabs.syncHeight(h);
33561             }
33562         }
33563         if(this.panelSize){
33564             w = w !== null ? w : this.panelSize.width;
33565             h = h !== null ? h : this.panelSize.height;
33566         }
33567         if(this.activePanel){
33568             var el = this.activePanel.getEl();
33569             w = w !== null ? w : el.getWidth();
33570             h = h !== null ? h : el.getHeight();
33571             this.panelSize = {width: w, height: h};
33572             this.activePanel.setSize(w, h);
33573         }
33574         if(Roo.isIE && this.tabs){
33575             this.tabs.el.repaint();
33576         }
33577     },
33578
33579     /**
33580      * Returns the container element for this region.
33581      * @return {Roo.Element}
33582      */
33583     getEl : function(){
33584         return this.el;
33585     },
33586
33587     /**
33588      * Hides this region.
33589      */
33590     hide : function(){
33591         //if(!this.collapsed){
33592             this.el.dom.style.left = "-2000px";
33593             this.el.hide();
33594         //}else{
33595          //   this.collapsedEl.dom.style.left = "-2000px";
33596          //   this.collapsedEl.hide();
33597        // }
33598         this.visible = false;
33599         this.fireEvent("visibilitychange", this, false);
33600     },
33601
33602     /**
33603      * Shows this region if it was previously hidden.
33604      */
33605     show : function(){
33606         //if(!this.collapsed){
33607             this.el.show();
33608         //}else{
33609         //    this.collapsedEl.show();
33610        // }
33611         this.visible = true;
33612         this.fireEvent("visibilitychange", this, true);
33613     },
33614 /*
33615     closeClicked : function(){
33616         if(this.activePanel){
33617             this.remove(this.activePanel);
33618         }
33619     },
33620
33621     collapseClick : function(e){
33622         if(this.isSlid){
33623            e.stopPropagation();
33624            this.slideIn();
33625         }else{
33626            e.stopPropagation();
33627            this.slideOut();
33628         }
33629     },
33630 */
33631     /**
33632      * Collapses this region.
33633      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33634      */
33635     /*
33636     collapse : function(skipAnim, skipCheck = false){
33637         if(this.collapsed) {
33638             return;
33639         }
33640         
33641         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
33642             
33643             this.collapsed = true;
33644             if(this.split){
33645                 this.split.el.hide();
33646             }
33647             if(this.config.animate && skipAnim !== true){
33648                 this.fireEvent("invalidated", this);
33649                 this.animateCollapse();
33650             }else{
33651                 this.el.setLocation(-20000,-20000);
33652                 this.el.hide();
33653                 this.collapsedEl.show();
33654                 this.fireEvent("collapsed", this);
33655                 this.fireEvent("invalidated", this);
33656             }
33657         }
33658         
33659     },
33660 */
33661     animateCollapse : function(){
33662         // overridden
33663     },
33664
33665     /**
33666      * Expands this region if it was previously collapsed.
33667      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33668      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33669      */
33670     /*
33671     expand : function(e, skipAnim){
33672         if(e) {
33673             e.stopPropagation();
33674         }
33675         if(!this.collapsed || this.el.hasActiveFx()) {
33676             return;
33677         }
33678         if(this.isSlid){
33679             this.afterSlideIn();
33680             skipAnim = true;
33681         }
33682         this.collapsed = false;
33683         if(this.config.animate && skipAnim !== true){
33684             this.animateExpand();
33685         }else{
33686             this.el.show();
33687             if(this.split){
33688                 this.split.el.show();
33689             }
33690             this.collapsedEl.setLocation(-2000,-2000);
33691             this.collapsedEl.hide();
33692             this.fireEvent("invalidated", this);
33693             this.fireEvent("expanded", this);
33694         }
33695     },
33696 */
33697     animateExpand : function(){
33698         // overridden
33699     },
33700
33701     initTabs : function()
33702     {
33703         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
33704         
33705         var ts = new Roo.bootstrap.panel.Tabs({
33706                 el: this.bodyEl.dom,
33707                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
33708                 disableTooltips: this.config.disableTabTips,
33709                 toolbar : this.config.toolbar
33710             });
33711         
33712         if(this.config.hideTabs){
33713             ts.stripWrap.setDisplayed(false);
33714         }
33715         this.tabs = ts;
33716         ts.resizeTabs = this.config.resizeTabs === true;
33717         ts.minTabWidth = this.config.minTabWidth || 40;
33718         ts.maxTabWidth = this.config.maxTabWidth || 250;
33719         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33720         ts.monitorResize = false;
33721         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
33722         ts.bodyEl.addClass('roo-layout-tabs-body');
33723         this.panels.each(this.initPanelAsTab, this);
33724     },
33725
33726     initPanelAsTab : function(panel){
33727         var ti = this.tabs.addTab(
33728             panel.getEl().id,
33729             panel.getTitle(),
33730             null,
33731             this.config.closeOnTab && panel.isClosable(),
33732             panel.tpl
33733         );
33734         if(panel.tabTip !== undefined){
33735             ti.setTooltip(panel.tabTip);
33736         }
33737         ti.on("activate", function(){
33738               this.setActivePanel(panel);
33739         }, this);
33740         
33741         if(this.config.closeOnTab){
33742             ti.on("beforeclose", function(t, e){
33743                 e.cancel = true;
33744                 this.remove(panel);
33745             }, this);
33746         }
33747         
33748         panel.tabItem = ti;
33749         
33750         return ti;
33751     },
33752
33753     updatePanelTitle : function(panel, title)
33754     {
33755         if(this.activePanel == panel){
33756             this.updateTitle(title);
33757         }
33758         if(this.tabs){
33759             var ti = this.tabs.getTab(panel.getEl().id);
33760             ti.setText(title);
33761             if(panel.tabTip !== undefined){
33762                 ti.setTooltip(panel.tabTip);
33763             }
33764         }
33765     },
33766
33767     updateTitle : function(title){
33768         if(this.titleTextEl && !this.config.title){
33769             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
33770         }
33771     },
33772
33773     setActivePanel : function(panel)
33774     {
33775         panel = this.getPanel(panel);
33776         if(this.activePanel && this.activePanel != panel){
33777             this.activePanel.setActiveState(false);
33778         }
33779         this.activePanel = panel;
33780         panel.setActiveState(true);
33781         if(this.panelSize){
33782             panel.setSize(this.panelSize.width, this.panelSize.height);
33783         }
33784         if(this.closeBtn){
33785             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33786         }
33787         this.updateTitle(panel.getTitle());
33788         if(this.tabs){
33789             this.fireEvent("invalidated", this);
33790         }
33791         this.fireEvent("panelactivated", this, panel);
33792     },
33793
33794     /**
33795      * Shows the specified panel.
33796      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33797      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33798      */
33799     showPanel : function(panel)
33800     {
33801         panel = this.getPanel(panel);
33802         if(panel){
33803             if(this.tabs){
33804                 var tab = this.tabs.getTab(panel.getEl().id);
33805                 if(tab.isHidden()){
33806                     this.tabs.unhideTab(tab.id);
33807                 }
33808                 tab.activate();
33809             }else{
33810                 this.setActivePanel(panel);
33811             }
33812         }
33813         return panel;
33814     },
33815
33816     /**
33817      * Get the active panel for this region.
33818      * @return {Roo.ContentPanel} The active panel or null
33819      */
33820     getActivePanel : function(){
33821         return this.activePanel;
33822     },
33823
33824     validateVisibility : function(){
33825         if(this.panels.getCount() < 1){
33826             this.updateTitle("&#160;");
33827             this.closeBtn.hide();
33828             this.hide();
33829         }else{
33830             if(!this.isVisible()){
33831                 this.show();
33832             }
33833         }
33834     },
33835
33836     /**
33837      * Adds the passed ContentPanel(s) to this region.
33838      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33839      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33840      */
33841     add : function(panel)
33842     {
33843         if(arguments.length > 1){
33844             for(var i = 0, len = arguments.length; i < len; i++) {
33845                 this.add(arguments[i]);
33846             }
33847             return null;
33848         }
33849         
33850         // if we have not been rendered yet, then we can not really do much of this..
33851         if (!this.bodyEl) {
33852             this.unrendered_panels.push(panel);
33853             return panel;
33854         }
33855         
33856         
33857         
33858         
33859         if(this.hasPanel(panel)){
33860             this.showPanel(panel);
33861             return panel;
33862         }
33863         panel.setRegion(this);
33864         this.panels.add(panel);
33865        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33866             // sinle panel - no tab...?? would it not be better to render it with the tabs,
33867             // and hide them... ???
33868             this.bodyEl.dom.appendChild(panel.getEl().dom);
33869             if(panel.background !== true){
33870                 this.setActivePanel(panel);
33871             }
33872             this.fireEvent("paneladded", this, panel);
33873             return panel;
33874         }
33875         */
33876         if(!this.tabs){
33877             this.initTabs();
33878         }else{
33879             this.initPanelAsTab(panel);
33880         }
33881         
33882         
33883         if(panel.background !== true){
33884             this.tabs.activate(panel.getEl().id);
33885         }
33886         this.fireEvent("paneladded", this, panel);
33887         return panel;
33888     },
33889
33890     /**
33891      * Hides the tab for the specified panel.
33892      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33893      */
33894     hidePanel : function(panel){
33895         if(this.tabs && (panel = this.getPanel(panel))){
33896             this.tabs.hideTab(panel.getEl().id);
33897         }
33898     },
33899
33900     /**
33901      * Unhides the tab for a previously hidden panel.
33902      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33903      */
33904     unhidePanel : function(panel){
33905         if(this.tabs && (panel = this.getPanel(panel))){
33906             this.tabs.unhideTab(panel.getEl().id);
33907         }
33908     },
33909
33910     clearPanels : function(){
33911         while(this.panels.getCount() > 0){
33912              this.remove(this.panels.first());
33913         }
33914     },
33915
33916     /**
33917      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33918      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33919      * @param {Boolean} preservePanel Overrides the config preservePanel option
33920      * @return {Roo.ContentPanel} The panel that was removed
33921      */
33922     remove : function(panel, preservePanel)
33923     {
33924         panel = this.getPanel(panel);
33925         if(!panel){
33926             return null;
33927         }
33928         var e = {};
33929         this.fireEvent("beforeremove", this, panel, e);
33930         if(e.cancel === true){
33931             return null;
33932         }
33933         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33934         var panelId = panel.getId();
33935         this.panels.removeKey(panelId);
33936         if(preservePanel){
33937             document.body.appendChild(panel.getEl().dom);
33938         }
33939         if(this.tabs){
33940             this.tabs.removeTab(panel.getEl().id);
33941         }else if (!preservePanel){
33942             this.bodyEl.dom.removeChild(panel.getEl().dom);
33943         }
33944         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33945             var p = this.panels.first();
33946             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33947             tempEl.appendChild(p.getEl().dom);
33948             this.bodyEl.update("");
33949             this.bodyEl.dom.appendChild(p.getEl().dom);
33950             tempEl = null;
33951             this.updateTitle(p.getTitle());
33952             this.tabs = null;
33953             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33954             this.setActivePanel(p);
33955         }
33956         panel.setRegion(null);
33957         if(this.activePanel == panel){
33958             this.activePanel = null;
33959         }
33960         if(this.config.autoDestroy !== false && preservePanel !== true){
33961             try{panel.destroy();}catch(e){}
33962         }
33963         this.fireEvent("panelremoved", this, panel);
33964         return panel;
33965     },
33966
33967     /**
33968      * Returns the TabPanel component used by this region
33969      * @return {Roo.TabPanel}
33970      */
33971     getTabs : function(){
33972         return this.tabs;
33973     },
33974
33975     createTool : function(parentEl, className){
33976         var btn = Roo.DomHelper.append(parentEl, {
33977             tag: "div",
33978             cls: "x-layout-tools-button",
33979             children: [ {
33980                 tag: "div",
33981                 cls: "roo-layout-tools-button-inner " + className,
33982                 html: "&#160;"
33983             }]
33984         }, true);
33985         btn.addClassOnOver("roo-layout-tools-button-over");
33986         return btn;
33987     }
33988 });/*
33989  * Based on:
33990  * Ext JS Library 1.1.1
33991  * Copyright(c) 2006-2007, Ext JS, LLC.
33992  *
33993  * Originally Released Under LGPL - original licence link has changed is not relivant.
33994  *
33995  * Fork - LGPL
33996  * <script type="text/javascript">
33997  */
33998  
33999
34000
34001 /**
34002  * @class Roo.SplitLayoutRegion
34003  * @extends Roo.LayoutRegion
34004  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34005  */
34006 Roo.bootstrap.layout.Split = function(config){
34007     this.cursor = config.cursor;
34008     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
34009 };
34010
34011 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
34012 {
34013     splitTip : "Drag to resize.",
34014     collapsibleSplitTip : "Drag to resize. Double click to hide.",
34015     useSplitTips : false,
34016
34017     applyConfig : function(config){
34018         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
34019     },
34020     
34021     onRender : function(ctr,pos) {
34022         
34023         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
34024         if(!this.config.split){
34025             return;
34026         }
34027         if(!this.split){
34028             
34029             var splitEl = Roo.DomHelper.append(ctr.dom,  {
34030                             tag: "div",
34031                             id: this.el.id + "-split",
34032                             cls: "roo-layout-split roo-layout-split-"+this.position,
34033                             html: "&#160;"
34034             });
34035             /** The SplitBar for this region 
34036             * @type Roo.SplitBar */
34037             // does not exist yet...
34038             Roo.log([this.position, this.orientation]);
34039             
34040             this.split = new Roo.bootstrap.SplitBar({
34041                 dragElement : splitEl,
34042                 resizingElement: this.el,
34043                 orientation : this.orientation
34044             });
34045             
34046             this.split.on("moved", this.onSplitMove, this);
34047             this.split.useShim = this.config.useShim === true;
34048             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34049             if(this.useSplitTips){
34050                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34051             }
34052             //if(config.collapsible){
34053             //    this.split.el.on("dblclick", this.collapse,  this);
34054             //}
34055         }
34056         if(typeof this.config.minSize != "undefined"){
34057             this.split.minSize = this.config.minSize;
34058         }
34059         if(typeof this.config.maxSize != "undefined"){
34060             this.split.maxSize = this.config.maxSize;
34061         }
34062         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
34063             this.hideSplitter();
34064         }
34065         
34066     },
34067
34068     getHMaxSize : function(){
34069          var cmax = this.config.maxSize || 10000;
34070          var center = this.mgr.getRegion("center");
34071          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34072     },
34073
34074     getVMaxSize : function(){
34075          var cmax = this.config.maxSize || 10000;
34076          var center = this.mgr.getRegion("center");
34077          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34078     },
34079
34080     onSplitMove : function(split, newSize){
34081         this.fireEvent("resized", this, newSize);
34082     },
34083     
34084     /** 
34085      * Returns the {@link Roo.SplitBar} for this region.
34086      * @return {Roo.SplitBar}
34087      */
34088     getSplitBar : function(){
34089         return this.split;
34090     },
34091     
34092     hide : function(){
34093         this.hideSplitter();
34094         Roo.bootstrap.layout.Split.superclass.hide.call(this);
34095     },
34096
34097     hideSplitter : function(){
34098         if(this.split){
34099             this.split.el.setLocation(-2000,-2000);
34100             this.split.el.hide();
34101         }
34102     },
34103
34104     show : function(){
34105         if(this.split){
34106             this.split.el.show();
34107         }
34108         Roo.bootstrap.layout.Split.superclass.show.call(this);
34109     },
34110     
34111     beforeSlide: function(){
34112         if(Roo.isGecko){// firefox overflow auto bug workaround
34113             this.bodyEl.clip();
34114             if(this.tabs) {
34115                 this.tabs.bodyEl.clip();
34116             }
34117             if(this.activePanel){
34118                 this.activePanel.getEl().clip();
34119                 
34120                 if(this.activePanel.beforeSlide){
34121                     this.activePanel.beforeSlide();
34122                 }
34123             }
34124         }
34125     },
34126     
34127     afterSlide : function(){
34128         if(Roo.isGecko){// firefox overflow auto bug workaround
34129             this.bodyEl.unclip();
34130             if(this.tabs) {
34131                 this.tabs.bodyEl.unclip();
34132             }
34133             if(this.activePanel){
34134                 this.activePanel.getEl().unclip();
34135                 if(this.activePanel.afterSlide){
34136                     this.activePanel.afterSlide();
34137                 }
34138             }
34139         }
34140     },
34141
34142     initAutoHide : function(){
34143         if(this.autoHide !== false){
34144             if(!this.autoHideHd){
34145                 var st = new Roo.util.DelayedTask(this.slideIn, this);
34146                 this.autoHideHd = {
34147                     "mouseout": function(e){
34148                         if(!e.within(this.el, true)){
34149                             st.delay(500);
34150                         }
34151                     },
34152                     "mouseover" : function(e){
34153                         st.cancel();
34154                     },
34155                     scope : this
34156                 };
34157             }
34158             this.el.on(this.autoHideHd);
34159         }
34160     },
34161
34162     clearAutoHide : function(){
34163         if(this.autoHide !== false){
34164             this.el.un("mouseout", this.autoHideHd.mouseout);
34165             this.el.un("mouseover", this.autoHideHd.mouseover);
34166         }
34167     },
34168
34169     clearMonitor : function(){
34170         Roo.get(document).un("click", this.slideInIf, this);
34171     },
34172
34173     // these names are backwards but not changed for compat
34174     slideOut : function(){
34175         if(this.isSlid || this.el.hasActiveFx()){
34176             return;
34177         }
34178         this.isSlid = true;
34179         if(this.collapseBtn){
34180             this.collapseBtn.hide();
34181         }
34182         this.closeBtnState = this.closeBtn.getStyle('display');
34183         this.closeBtn.hide();
34184         if(this.stickBtn){
34185             this.stickBtn.show();
34186         }
34187         this.el.show();
34188         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34189         this.beforeSlide();
34190         this.el.setStyle("z-index", 10001);
34191         this.el.slideIn(this.getSlideAnchor(), {
34192             callback: function(){
34193                 this.afterSlide();
34194                 this.initAutoHide();
34195                 Roo.get(document).on("click", this.slideInIf, this);
34196                 this.fireEvent("slideshow", this);
34197             },
34198             scope: this,
34199             block: true
34200         });
34201     },
34202
34203     afterSlideIn : function(){
34204         this.clearAutoHide();
34205         this.isSlid = false;
34206         this.clearMonitor();
34207         this.el.setStyle("z-index", "");
34208         if(this.collapseBtn){
34209             this.collapseBtn.show();
34210         }
34211         this.closeBtn.setStyle('display', this.closeBtnState);
34212         if(this.stickBtn){
34213             this.stickBtn.hide();
34214         }
34215         this.fireEvent("slidehide", this);
34216     },
34217
34218     slideIn : function(cb){
34219         if(!this.isSlid || this.el.hasActiveFx()){
34220             Roo.callback(cb);
34221             return;
34222         }
34223         this.isSlid = false;
34224         this.beforeSlide();
34225         this.el.slideOut(this.getSlideAnchor(), {
34226             callback: function(){
34227                 this.el.setLeftTop(-10000, -10000);
34228                 this.afterSlide();
34229                 this.afterSlideIn();
34230                 Roo.callback(cb);
34231             },
34232             scope: this,
34233             block: true
34234         });
34235     },
34236     
34237     slideInIf : function(e){
34238         if(!e.within(this.el)){
34239             this.slideIn();
34240         }
34241     },
34242
34243     animateCollapse : function(){
34244         this.beforeSlide();
34245         this.el.setStyle("z-index", 20000);
34246         var anchor = this.getSlideAnchor();
34247         this.el.slideOut(anchor, {
34248             callback : function(){
34249                 this.el.setStyle("z-index", "");
34250                 this.collapsedEl.slideIn(anchor, {duration:.3});
34251                 this.afterSlide();
34252                 this.el.setLocation(-10000,-10000);
34253                 this.el.hide();
34254                 this.fireEvent("collapsed", this);
34255             },
34256             scope: this,
34257             block: true
34258         });
34259     },
34260
34261     animateExpand : function(){
34262         this.beforeSlide();
34263         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34264         this.el.setStyle("z-index", 20000);
34265         this.collapsedEl.hide({
34266             duration:.1
34267         });
34268         this.el.slideIn(this.getSlideAnchor(), {
34269             callback : function(){
34270                 this.el.setStyle("z-index", "");
34271                 this.afterSlide();
34272                 if(this.split){
34273                     this.split.el.show();
34274                 }
34275                 this.fireEvent("invalidated", this);
34276                 this.fireEvent("expanded", this);
34277             },
34278             scope: this,
34279             block: true
34280         });
34281     },
34282
34283     anchors : {
34284         "west" : "left",
34285         "east" : "right",
34286         "north" : "top",
34287         "south" : "bottom"
34288     },
34289
34290     sanchors : {
34291         "west" : "l",
34292         "east" : "r",
34293         "north" : "t",
34294         "south" : "b"
34295     },
34296
34297     canchors : {
34298         "west" : "tl-tr",
34299         "east" : "tr-tl",
34300         "north" : "tl-bl",
34301         "south" : "bl-tl"
34302     },
34303
34304     getAnchor : function(){
34305         return this.anchors[this.position];
34306     },
34307
34308     getCollapseAnchor : function(){
34309         return this.canchors[this.position];
34310     },
34311
34312     getSlideAnchor : function(){
34313         return this.sanchors[this.position];
34314     },
34315
34316     getAlignAdj : function(){
34317         var cm = this.cmargins;
34318         switch(this.position){
34319             case "west":
34320                 return [0, 0];
34321             break;
34322             case "east":
34323                 return [0, 0];
34324             break;
34325             case "north":
34326                 return [0, 0];
34327             break;
34328             case "south":
34329                 return [0, 0];
34330             break;
34331         }
34332     },
34333
34334     getExpandAdj : function(){
34335         var c = this.collapsedEl, cm = this.cmargins;
34336         switch(this.position){
34337             case "west":
34338                 return [-(cm.right+c.getWidth()+cm.left), 0];
34339             break;
34340             case "east":
34341                 return [cm.right+c.getWidth()+cm.left, 0];
34342             break;
34343             case "north":
34344                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34345             break;
34346             case "south":
34347                 return [0, cm.top+cm.bottom+c.getHeight()];
34348             break;
34349         }
34350     }
34351 });/*
34352  * Based on:
34353  * Ext JS Library 1.1.1
34354  * Copyright(c) 2006-2007, Ext JS, LLC.
34355  *
34356  * Originally Released Under LGPL - original licence link has changed is not relivant.
34357  *
34358  * Fork - LGPL
34359  * <script type="text/javascript">
34360  */
34361 /*
34362  * These classes are private internal classes
34363  */
34364 Roo.bootstrap.layout.Center = function(config){
34365     config.region = "center";
34366     Roo.bootstrap.layout.Region.call(this, config);
34367     this.visible = true;
34368     this.minWidth = config.minWidth || 20;
34369     this.minHeight = config.minHeight || 20;
34370 };
34371
34372 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
34373     hide : function(){
34374         // center panel can't be hidden
34375     },
34376     
34377     show : function(){
34378         // center panel can't be hidden
34379     },
34380     
34381     getMinWidth: function(){
34382         return this.minWidth;
34383     },
34384     
34385     getMinHeight: function(){
34386         return this.minHeight;
34387     }
34388 });
34389
34390
34391
34392
34393  
34394
34395
34396
34397
34398
34399 Roo.bootstrap.layout.North = function(config)
34400 {
34401     config.region = 'north';
34402     config.cursor = 'n-resize';
34403     
34404     Roo.bootstrap.layout.Split.call(this, config);
34405     
34406     
34407     if(this.split){
34408         this.split.placement = Roo.bootstrap.SplitBar.TOP;
34409         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34410         this.split.el.addClass("roo-layout-split-v");
34411     }
34412     var size = config.initialSize || config.height;
34413     if(typeof size != "undefined"){
34414         this.el.setHeight(size);
34415     }
34416 };
34417 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
34418 {
34419     orientation: Roo.bootstrap.SplitBar.VERTICAL,
34420     
34421     
34422     
34423     getBox : function(){
34424         if(this.collapsed){
34425             return this.collapsedEl.getBox();
34426         }
34427         var box = this.el.getBox();
34428         if(this.split){
34429             box.height += this.split.el.getHeight();
34430         }
34431         return box;
34432     },
34433     
34434     updateBox : function(box){
34435         if(this.split && !this.collapsed){
34436             box.height -= this.split.el.getHeight();
34437             this.split.el.setLeft(box.x);
34438             this.split.el.setTop(box.y+box.height);
34439             this.split.el.setWidth(box.width);
34440         }
34441         if(this.collapsed){
34442             this.updateBody(box.width, null);
34443         }
34444         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34445     }
34446 });
34447
34448
34449
34450
34451
34452 Roo.bootstrap.layout.South = function(config){
34453     config.region = 'south';
34454     config.cursor = 's-resize';
34455     Roo.bootstrap.layout.Split.call(this, config);
34456     if(this.split){
34457         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
34458         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34459         this.split.el.addClass("roo-layout-split-v");
34460     }
34461     var size = config.initialSize || config.height;
34462     if(typeof size != "undefined"){
34463         this.el.setHeight(size);
34464     }
34465 };
34466
34467 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
34468     orientation: Roo.bootstrap.SplitBar.VERTICAL,
34469     getBox : function(){
34470         if(this.collapsed){
34471             return this.collapsedEl.getBox();
34472         }
34473         var box = this.el.getBox();
34474         if(this.split){
34475             var sh = this.split.el.getHeight();
34476             box.height += sh;
34477             box.y -= sh;
34478         }
34479         return box;
34480     },
34481     
34482     updateBox : function(box){
34483         if(this.split && !this.collapsed){
34484             var sh = this.split.el.getHeight();
34485             box.height -= sh;
34486             box.y += sh;
34487             this.split.el.setLeft(box.x);
34488             this.split.el.setTop(box.y-sh);
34489             this.split.el.setWidth(box.width);
34490         }
34491         if(this.collapsed){
34492             this.updateBody(box.width, null);
34493         }
34494         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34495     }
34496 });
34497
34498 Roo.bootstrap.layout.East = function(config){
34499     config.region = "east";
34500     config.cursor = "e-resize";
34501     Roo.bootstrap.layout.Split.call(this, config);
34502     if(this.split){
34503         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
34504         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34505         this.split.el.addClass("roo-layout-split-h");
34506     }
34507     var size = config.initialSize || config.width;
34508     if(typeof size != "undefined"){
34509         this.el.setWidth(size);
34510     }
34511 };
34512 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
34513     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34514     getBox : function(){
34515         if(this.collapsed){
34516             return this.collapsedEl.getBox();
34517         }
34518         var box = this.el.getBox();
34519         if(this.split){
34520             var sw = this.split.el.getWidth();
34521             box.width += sw;
34522             box.x -= sw;
34523         }
34524         return box;
34525     },
34526
34527     updateBox : function(box){
34528         if(this.split && !this.collapsed){
34529             var sw = this.split.el.getWidth();
34530             box.width -= sw;
34531             this.split.el.setLeft(box.x);
34532             this.split.el.setTop(box.y);
34533             this.split.el.setHeight(box.height);
34534             box.x += sw;
34535         }
34536         if(this.collapsed){
34537             this.updateBody(null, box.height);
34538         }
34539         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34540     }
34541 });
34542
34543 Roo.bootstrap.layout.West = function(config){
34544     config.region = "west";
34545     config.cursor = "w-resize";
34546     
34547     Roo.bootstrap.layout.Split.call(this, config);
34548     if(this.split){
34549         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
34550         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34551         this.split.el.addClass("roo-layout-split-h");
34552     }
34553     
34554 };
34555 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
34556     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34557     
34558     onRender: function(ctr, pos)
34559     {
34560         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
34561         var size = this.config.initialSize || this.config.width;
34562         if(typeof size != "undefined"){
34563             this.el.setWidth(size);
34564         }
34565     },
34566     
34567     getBox : function(){
34568         if(this.collapsed){
34569             return this.collapsedEl.getBox();
34570         }
34571         var box = this.el.getBox();
34572         if(this.split){
34573             box.width += this.split.el.getWidth();
34574         }
34575         return box;
34576     },
34577     
34578     updateBox : function(box){
34579         if(this.split && !this.collapsed){
34580             var sw = this.split.el.getWidth();
34581             box.width -= sw;
34582             this.split.el.setLeft(box.x+box.width);
34583             this.split.el.setTop(box.y);
34584             this.split.el.setHeight(box.height);
34585         }
34586         if(this.collapsed){
34587             this.updateBody(null, box.height);
34588         }
34589         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34590     }
34591 });
34592 Roo.namespace("Roo.bootstrap.panel");/*
34593  * Based on:
34594  * Ext JS Library 1.1.1
34595  * Copyright(c) 2006-2007, Ext JS, LLC.
34596  *
34597  * Originally Released Under LGPL - original licence link has changed is not relivant.
34598  *
34599  * Fork - LGPL
34600  * <script type="text/javascript">
34601  */
34602 /**
34603  * @class Roo.ContentPanel
34604  * @extends Roo.util.Observable
34605  * A basic ContentPanel element.
34606  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
34607  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
34608  * @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
34609  * @cfg {Boolean}   closable      True if the panel can be closed/removed
34610  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
34611  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34612  * @cfg {Toolbar}   toolbar       A toolbar for this panel
34613  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
34614  * @cfg {String} title          The title for this panel
34615  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34616  * @cfg {String} url            Calls {@link #setUrl} with this value
34617  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34618  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
34619  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
34620  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
34621  * @cfg {Boolean} badges render the badges
34622
34623  * @constructor
34624  * Create a new ContentPanel.
34625  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34626  * @param {String/Object} config A string to set only the title or a config object
34627  * @param {String} content (optional) Set the HTML content for this panel
34628  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34629  */
34630 Roo.bootstrap.panel.Content = function( config){
34631     
34632     this.tpl = config.tpl || false;
34633     
34634     var el = config.el;
34635     var content = config.content;
34636
34637     if(config.autoCreate){ // xtype is available if this is called from factory
34638         el = Roo.id();
34639     }
34640     this.el = Roo.get(el);
34641     if(!this.el && config && config.autoCreate){
34642         if(typeof config.autoCreate == "object"){
34643             if(!config.autoCreate.id){
34644                 config.autoCreate.id = config.id||el;
34645             }
34646             this.el = Roo.DomHelper.append(document.body,
34647                         config.autoCreate, true);
34648         }else{
34649             var elcfg =  {   tag: "div",
34650                             cls: "roo-layout-inactive-content",
34651                             id: config.id||el
34652                             };
34653             if (config.html) {
34654                 elcfg.html = config.html;
34655                 
34656             }
34657                         
34658             this.el = Roo.DomHelper.append(document.body, elcfg , true);
34659         }
34660     } 
34661     this.closable = false;
34662     this.loaded = false;
34663     this.active = false;
34664    
34665       
34666     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
34667         
34668         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
34669         
34670         this.wrapEl = this.el; //this.el.wrap();
34671         var ti = [];
34672         if (config.toolbar.items) {
34673             ti = config.toolbar.items ;
34674             delete config.toolbar.items ;
34675         }
34676         
34677         var nitems = [];
34678         this.toolbar.render(this.wrapEl, 'before');
34679         for(var i =0;i < ti.length;i++) {
34680           //  Roo.log(['add child', items[i]]);
34681             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34682         }
34683         this.toolbar.items = nitems;
34684         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
34685         delete config.toolbar;
34686         
34687     }
34688     /*
34689     // xtype created footer. - not sure if will work as we normally have to render first..
34690     if (this.footer && !this.footer.el && this.footer.xtype) {
34691         if (!this.wrapEl) {
34692             this.wrapEl = this.el.wrap();
34693         }
34694     
34695         this.footer.container = this.wrapEl.createChild();
34696          
34697         this.footer = Roo.factory(this.footer, Roo);
34698         
34699     }
34700     */
34701     
34702      if(typeof config == "string"){
34703         this.title = config;
34704     }else{
34705         Roo.apply(this, config);
34706     }
34707     
34708     if(this.resizeEl){
34709         this.resizeEl = Roo.get(this.resizeEl, true);
34710     }else{
34711         this.resizeEl = this.el;
34712     }
34713     // handle view.xtype
34714     
34715  
34716     
34717     
34718     this.addEvents({
34719         /**
34720          * @event activate
34721          * Fires when this panel is activated. 
34722          * @param {Roo.ContentPanel} this
34723          */
34724         "activate" : true,
34725         /**
34726          * @event deactivate
34727          * Fires when this panel is activated. 
34728          * @param {Roo.ContentPanel} this
34729          */
34730         "deactivate" : true,
34731
34732         /**
34733          * @event resize
34734          * Fires when this panel is resized if fitToFrame is true.
34735          * @param {Roo.ContentPanel} this
34736          * @param {Number} width The width after any component adjustments
34737          * @param {Number} height The height after any component adjustments
34738          */
34739         "resize" : true,
34740         
34741          /**
34742          * @event render
34743          * Fires when this tab is created
34744          * @param {Roo.ContentPanel} this
34745          */
34746         "render" : true
34747         
34748         
34749         
34750     });
34751     
34752
34753     
34754     
34755     if(this.autoScroll){
34756         this.resizeEl.setStyle("overflow", "auto");
34757     } else {
34758         // fix randome scrolling
34759         //this.el.on('scroll', function() {
34760         //    Roo.log('fix random scolling');
34761         //    this.scrollTo('top',0); 
34762         //});
34763     }
34764     content = content || this.content;
34765     if(content){
34766         this.setContent(content);
34767     }
34768     if(config && config.url){
34769         this.setUrl(this.url, this.params, this.loadOnce);
34770     }
34771     
34772     
34773     
34774     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
34775     
34776     if (this.view && typeof(this.view.xtype) != 'undefined') {
34777         this.view.el = this.el.appendChild(document.createElement("div"));
34778         this.view = Roo.factory(this.view); 
34779         this.view.render  &&  this.view.render(false, '');  
34780     }
34781     
34782     
34783     this.fireEvent('render', this);
34784 };
34785
34786 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
34787     
34788     tabTip : '',
34789     
34790     setRegion : function(region){
34791         this.region = region;
34792         this.setActiveClass(region && !this.background);
34793     },
34794     
34795     
34796     setActiveClass: function(state)
34797     {
34798         if(state){
34799            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
34800            this.el.setStyle('position','relative');
34801         }else{
34802            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
34803            this.el.setStyle('position', 'absolute');
34804         } 
34805     },
34806     
34807     /**
34808      * Returns the toolbar for this Panel if one was configured. 
34809      * @return {Roo.Toolbar} 
34810      */
34811     getToolbar : function(){
34812         return this.toolbar;
34813     },
34814     
34815     setActiveState : function(active)
34816     {
34817         this.active = active;
34818         this.setActiveClass(active);
34819         if(!active){
34820             this.fireEvent("deactivate", this);
34821         }else{
34822             this.fireEvent("activate", this);
34823         }
34824     },
34825     /**
34826      * Updates this panel's element
34827      * @param {String} content The new content
34828      * @param {Boolean} loadScripts (optional) true to look for and process scripts
34829     */
34830     setContent : function(content, loadScripts){
34831         this.el.update(content, loadScripts);
34832     },
34833
34834     ignoreResize : function(w, h){
34835         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34836             return true;
34837         }else{
34838             this.lastSize = {width: w, height: h};
34839             return false;
34840         }
34841     },
34842     /**
34843      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34844      * @return {Roo.UpdateManager} The UpdateManager
34845      */
34846     getUpdateManager : function(){
34847         return this.el.getUpdateManager();
34848     },
34849      /**
34850      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34851      * @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:
34852 <pre><code>
34853 panel.load({
34854     url: "your-url.php",
34855     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34856     callback: yourFunction,
34857     scope: yourObject, //(optional scope)
34858     discardUrl: false,
34859     nocache: false,
34860     text: "Loading...",
34861     timeout: 30,
34862     scripts: false
34863 });
34864 </code></pre>
34865      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34866      * 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.
34867      * @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}
34868      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34869      * @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.
34870      * @return {Roo.ContentPanel} this
34871      */
34872     load : function(){
34873         var um = this.el.getUpdateManager();
34874         um.update.apply(um, arguments);
34875         return this;
34876     },
34877
34878
34879     /**
34880      * 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.
34881      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34882      * @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)
34883      * @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)
34884      * @return {Roo.UpdateManager} The UpdateManager
34885      */
34886     setUrl : function(url, params, loadOnce){
34887         if(this.refreshDelegate){
34888             this.removeListener("activate", this.refreshDelegate);
34889         }
34890         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34891         this.on("activate", this.refreshDelegate);
34892         return this.el.getUpdateManager();
34893     },
34894     
34895     _handleRefresh : function(url, params, loadOnce){
34896         if(!loadOnce || !this.loaded){
34897             var updater = this.el.getUpdateManager();
34898             updater.update(url, params, this._setLoaded.createDelegate(this));
34899         }
34900     },
34901     
34902     _setLoaded : function(){
34903         this.loaded = true;
34904     }, 
34905     
34906     /**
34907      * Returns this panel's id
34908      * @return {String} 
34909      */
34910     getId : function(){
34911         return this.el.id;
34912     },
34913     
34914     /** 
34915      * Returns this panel's element - used by regiosn to add.
34916      * @return {Roo.Element} 
34917      */
34918     getEl : function(){
34919         return this.wrapEl || this.el;
34920     },
34921     
34922    
34923     
34924     adjustForComponents : function(width, height)
34925     {
34926         //Roo.log('adjustForComponents ');
34927         if(this.resizeEl != this.el){
34928             width -= this.el.getFrameWidth('lr');
34929             height -= this.el.getFrameWidth('tb');
34930         }
34931         if(this.toolbar){
34932             var te = this.toolbar.getEl();
34933             height -= te.getHeight();
34934             te.setWidth(width);
34935         }
34936         if(this.footer){
34937             var te = this.footer.getEl();
34938             Roo.log("footer:" + te.getHeight());
34939             
34940             height -= te.getHeight();
34941             te.setWidth(width);
34942         }
34943         
34944         
34945         if(this.adjustments){
34946             width += this.adjustments[0];
34947             height += this.adjustments[1];
34948         }
34949         return {"width": width, "height": height};
34950     },
34951     
34952     setSize : function(width, height){
34953         if(this.fitToFrame && !this.ignoreResize(width, height)){
34954             if(this.fitContainer && this.resizeEl != this.el){
34955                 this.el.setSize(width, height);
34956             }
34957             var size = this.adjustForComponents(width, height);
34958             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34959             this.fireEvent('resize', this, size.width, size.height);
34960         }
34961     },
34962     
34963     /**
34964      * Returns this panel's title
34965      * @return {String} 
34966      */
34967     getTitle : function(){
34968         return this.title;
34969     },
34970     
34971     /**
34972      * Set this panel's title
34973      * @param {String} title
34974      */
34975     setTitle : function(title){
34976         this.title = title;
34977         if(this.region){
34978             this.region.updatePanelTitle(this, title);
34979         }
34980     },
34981     
34982     /**
34983      * Returns true is this panel was configured to be closable
34984      * @return {Boolean} 
34985      */
34986     isClosable : function(){
34987         return this.closable;
34988     },
34989     
34990     beforeSlide : function(){
34991         this.el.clip();
34992         this.resizeEl.clip();
34993     },
34994     
34995     afterSlide : function(){
34996         this.el.unclip();
34997         this.resizeEl.unclip();
34998     },
34999     
35000     /**
35001      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
35002      *   Will fail silently if the {@link #setUrl} method has not been called.
35003      *   This does not activate the panel, just updates its content.
35004      */
35005     refresh : function(){
35006         if(this.refreshDelegate){
35007            this.loaded = false;
35008            this.refreshDelegate();
35009         }
35010     },
35011     
35012     /**
35013      * Destroys this panel
35014      */
35015     destroy : function(){
35016         this.el.removeAllListeners();
35017         var tempEl = document.createElement("span");
35018         tempEl.appendChild(this.el.dom);
35019         tempEl.innerHTML = "";
35020         this.el.remove();
35021         this.el = null;
35022     },
35023     
35024     /**
35025      * form - if the content panel contains a form - this is a reference to it.
35026      * @type {Roo.form.Form}
35027      */
35028     form : false,
35029     /**
35030      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35031      *    This contains a reference to it.
35032      * @type {Roo.View}
35033      */
35034     view : false,
35035     
35036       /**
35037      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35038      * <pre><code>
35039
35040 layout.addxtype({
35041        xtype : 'Form',
35042        items: [ .... ]
35043    }
35044 );
35045
35046 </code></pre>
35047      * @param {Object} cfg Xtype definition of item to add.
35048      */
35049     
35050     
35051     getChildContainer: function () {
35052         return this.getEl();
35053     }
35054     
35055     
35056     /*
35057         var  ret = new Roo.factory(cfg);
35058         return ret;
35059         
35060         
35061         // add form..
35062         if (cfg.xtype.match(/^Form$/)) {
35063             
35064             var el;
35065             //if (this.footer) {
35066             //    el = this.footer.container.insertSibling(false, 'before');
35067             //} else {
35068                 el = this.el.createChild();
35069             //}
35070
35071             this.form = new  Roo.form.Form(cfg);
35072             
35073             
35074             if ( this.form.allItems.length) {
35075                 this.form.render(el.dom);
35076             }
35077             return this.form;
35078         }
35079         // should only have one of theses..
35080         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35081             // views.. should not be just added - used named prop 'view''
35082             
35083             cfg.el = this.el.appendChild(document.createElement("div"));
35084             // factory?
35085             
35086             var ret = new Roo.factory(cfg);
35087              
35088              ret.render && ret.render(false, ''); // render blank..
35089             this.view = ret;
35090             return ret;
35091         }
35092         return false;
35093     }
35094     \*/
35095 });
35096  
35097 /**
35098  * @class Roo.bootstrap.panel.Grid
35099  * @extends Roo.bootstrap.panel.Content
35100  * @constructor
35101  * Create a new GridPanel.
35102  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
35103  * @param {Object} config A the config object
35104   
35105  */
35106
35107
35108
35109 Roo.bootstrap.panel.Grid = function(config)
35110 {
35111     
35112       
35113     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35114         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
35115
35116     config.el = this.wrapper;
35117     //this.el = this.wrapper;
35118     
35119       if (config.container) {
35120         // ctor'ed from a Border/panel.grid
35121         
35122         
35123         this.wrapper.setStyle("overflow", "hidden");
35124         this.wrapper.addClass('roo-grid-container');
35125
35126     }
35127     
35128     
35129     if(config.toolbar){
35130         var tool_el = this.wrapper.createChild();    
35131         this.toolbar = Roo.factory(config.toolbar);
35132         var ti = [];
35133         if (config.toolbar.items) {
35134             ti = config.toolbar.items ;
35135             delete config.toolbar.items ;
35136         }
35137         
35138         var nitems = [];
35139         this.toolbar.render(tool_el);
35140         for(var i =0;i < ti.length;i++) {
35141           //  Roo.log(['add child', items[i]]);
35142             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
35143         }
35144         this.toolbar.items = nitems;
35145         
35146         delete config.toolbar;
35147     }
35148     
35149     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
35150     config.grid.scrollBody = true;;
35151     config.grid.monitorWindowResize = false; // turn off autosizing
35152     config.grid.autoHeight = false;
35153     config.grid.autoWidth = false;
35154     
35155     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
35156     
35157     if (config.background) {
35158         // render grid on panel activation (if panel background)
35159         this.on('activate', function(gp) {
35160             if (!gp.grid.rendered) {
35161                 gp.grid.render(this.wrapper);
35162                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
35163             }
35164         });
35165             
35166     } else {
35167         this.grid.render(this.wrapper);
35168         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
35169
35170     }
35171     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
35172     // ??? needed ??? config.el = this.wrapper;
35173     
35174     
35175     
35176   
35177     // xtype created footer. - not sure if will work as we normally have to render first..
35178     if (this.footer && !this.footer.el && this.footer.xtype) {
35179         
35180         var ctr = this.grid.getView().getFooterPanel(true);
35181         this.footer.dataSource = this.grid.dataSource;
35182         this.footer = Roo.factory(this.footer, Roo);
35183         this.footer.render(ctr);
35184         
35185     }
35186     
35187     
35188     
35189     
35190      
35191 };
35192
35193 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
35194     getId : function(){
35195         return this.grid.id;
35196     },
35197     
35198     /**
35199      * Returns the grid for this panel
35200      * @return {Roo.bootstrap.Table} 
35201      */
35202     getGrid : function(){
35203         return this.grid;    
35204     },
35205     
35206     setSize : function(width, height){
35207         if(!this.ignoreResize(width, height)){
35208             var grid = this.grid;
35209             var size = this.adjustForComponents(width, height);
35210             var gridel = grid.getGridEl();
35211             gridel.setSize(size.width, size.height);
35212             /*
35213             var thd = grid.getGridEl().select('thead',true).first();
35214             var tbd = grid.getGridEl().select('tbody', true).first();
35215             if (tbd) {
35216                 tbd.setSize(width, height - thd.getHeight());
35217             }
35218             */
35219             grid.autoSize();
35220         }
35221     },
35222      
35223     
35224     
35225     beforeSlide : function(){
35226         this.grid.getView().scroller.clip();
35227     },
35228     
35229     afterSlide : function(){
35230         this.grid.getView().scroller.unclip();
35231     },
35232     
35233     destroy : function(){
35234         this.grid.destroy();
35235         delete this.grid;
35236         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
35237     }
35238 });
35239
35240 /**
35241  * @class Roo.bootstrap.panel.Nest
35242  * @extends Roo.bootstrap.panel.Content
35243  * @constructor
35244  * Create a new Panel, that can contain a layout.Border.
35245  * 
35246  * 
35247  * @param {Roo.BorderLayout} layout The layout for this panel
35248  * @param {String/Object} config A string to set only the title or a config object
35249  */
35250 Roo.bootstrap.panel.Nest = function(config)
35251 {
35252     // construct with only one argument..
35253     /* FIXME - implement nicer consturctors
35254     if (layout.layout) {
35255         config = layout;
35256         layout = config.layout;
35257         delete config.layout;
35258     }
35259     if (layout.xtype && !layout.getEl) {
35260         // then layout needs constructing..
35261         layout = Roo.factory(layout, Roo);
35262     }
35263     */
35264     
35265     config.el =  config.layout.getEl();
35266     
35267     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
35268     
35269     config.layout.monitorWindowResize = false; // turn off autosizing
35270     this.layout = config.layout;
35271     this.layout.getEl().addClass("roo-layout-nested-layout");
35272     
35273     
35274     
35275     
35276 };
35277
35278 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
35279
35280     setSize : function(width, height){
35281         if(!this.ignoreResize(width, height)){
35282             var size = this.adjustForComponents(width, height);
35283             var el = this.layout.getEl();
35284             if (size.height < 1) {
35285                 el.setWidth(size.width);   
35286             } else {
35287                 el.setSize(size.width, size.height);
35288             }
35289             var touch = el.dom.offsetWidth;
35290             this.layout.layout();
35291             // ie requires a double layout on the first pass
35292             if(Roo.isIE && !this.initialized){
35293                 this.initialized = true;
35294                 this.layout.layout();
35295             }
35296         }
35297     },
35298     
35299     // activate all subpanels if not currently active..
35300     
35301     setActiveState : function(active){
35302         this.active = active;
35303         this.setActiveClass(active);
35304         
35305         if(!active){
35306             this.fireEvent("deactivate", this);
35307             return;
35308         }
35309         
35310         this.fireEvent("activate", this);
35311         // not sure if this should happen before or after..
35312         if (!this.layout) {
35313             return; // should not happen..
35314         }
35315         var reg = false;
35316         for (var r in this.layout.regions) {
35317             reg = this.layout.getRegion(r);
35318             if (reg.getActivePanel()) {
35319                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35320                 reg.setActivePanel(reg.getActivePanel());
35321                 continue;
35322             }
35323             if (!reg.panels.length) {
35324                 continue;
35325             }
35326             reg.showPanel(reg.getPanel(0));
35327         }
35328         
35329         
35330         
35331         
35332     },
35333     
35334     /**
35335      * Returns the nested BorderLayout for this panel
35336      * @return {Roo.BorderLayout} 
35337      */
35338     getLayout : function(){
35339         return this.layout;
35340     },
35341     
35342      /**
35343      * Adds a xtype elements to the layout of the nested panel
35344      * <pre><code>
35345
35346 panel.addxtype({
35347        xtype : 'ContentPanel',
35348        region: 'west',
35349        items: [ .... ]
35350    }
35351 );
35352
35353 panel.addxtype({
35354         xtype : 'NestedLayoutPanel',
35355         region: 'west',
35356         layout: {
35357            center: { },
35358            west: { }   
35359         },
35360         items : [ ... list of content panels or nested layout panels.. ]
35361    }
35362 );
35363 </code></pre>
35364      * @param {Object} cfg Xtype definition of item to add.
35365      */
35366     addxtype : function(cfg) {
35367         return this.layout.addxtype(cfg);
35368     
35369     }
35370 });        /*
35371  * Based on:
35372  * Ext JS Library 1.1.1
35373  * Copyright(c) 2006-2007, Ext JS, LLC.
35374  *
35375  * Originally Released Under LGPL - original licence link has changed is not relivant.
35376  *
35377  * Fork - LGPL
35378  * <script type="text/javascript">
35379  */
35380 /**
35381  * @class Roo.TabPanel
35382  * @extends Roo.util.Observable
35383  * A lightweight tab container.
35384  * <br><br>
35385  * Usage:
35386  * <pre><code>
35387 // basic tabs 1, built from existing content
35388 var tabs = new Roo.TabPanel("tabs1");
35389 tabs.addTab("script", "View Script");
35390 tabs.addTab("markup", "View Markup");
35391 tabs.activate("script");
35392
35393 // more advanced tabs, built from javascript
35394 var jtabs = new Roo.TabPanel("jtabs");
35395 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
35396
35397 // set up the UpdateManager
35398 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
35399 var updater = tab2.getUpdateManager();
35400 updater.setDefaultUrl("ajax1.htm");
35401 tab2.on('activate', updater.refresh, updater, true);
35402
35403 // Use setUrl for Ajax loading
35404 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
35405 tab3.setUrl("ajax2.htm", null, true);
35406
35407 // Disabled tab
35408 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
35409 tab4.disable();
35410
35411 jtabs.activate("jtabs-1");
35412  * </code></pre>
35413  * @constructor
35414  * Create a new TabPanel.
35415  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
35416  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
35417  */
35418 Roo.bootstrap.panel.Tabs = function(config){
35419     /**
35420     * The container element for this TabPanel.
35421     * @type Roo.Element
35422     */
35423     this.el = Roo.get(config.el);
35424     delete config.el;
35425     if(config){
35426         if(typeof config == "boolean"){
35427             this.tabPosition = config ? "bottom" : "top";
35428         }else{
35429             Roo.apply(this, config);
35430         }
35431     }
35432     
35433     if(this.tabPosition == "bottom"){
35434         this.bodyEl = Roo.get(this.createBody(this.el.dom));
35435         this.el.addClass("roo-tabs-bottom");
35436     }
35437     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
35438     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
35439     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
35440     if(Roo.isIE){
35441         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
35442     }
35443     if(this.tabPosition != "bottom"){
35444         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
35445          * @type Roo.Element
35446          */
35447         this.bodyEl = Roo.get(this.createBody(this.el.dom));
35448         this.el.addClass("roo-tabs-top");
35449     }
35450     this.items = [];
35451
35452     this.bodyEl.setStyle("position", "relative");
35453
35454     this.active = null;
35455     this.activateDelegate = this.activate.createDelegate(this);
35456
35457     this.addEvents({
35458         /**
35459          * @event tabchange
35460          * Fires when the active tab changes
35461          * @param {Roo.TabPanel} this
35462          * @param {Roo.TabPanelItem} activePanel The new active tab
35463          */
35464         "tabchange": true,
35465         /**
35466          * @event beforetabchange
35467          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
35468          * @param {Roo.TabPanel} this
35469          * @param {Object} e Set cancel to true on this object to cancel the tab change
35470          * @param {Roo.TabPanelItem} tab The tab being changed to
35471          */
35472         "beforetabchange" : true
35473     });
35474
35475     Roo.EventManager.onWindowResize(this.onResize, this);
35476     this.cpad = this.el.getPadding("lr");
35477     this.hiddenCount = 0;
35478
35479
35480     // toolbar on the tabbar support...
35481     if (this.toolbar) {
35482         alert("no toolbar support yet");
35483         this.toolbar  = false;
35484         /*
35485         var tcfg = this.toolbar;
35486         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
35487         this.toolbar = new Roo.Toolbar(tcfg);
35488         if (Roo.isSafari) {
35489             var tbl = tcfg.container.child('table', true);
35490             tbl.setAttribute('width', '100%');
35491         }
35492         */
35493         
35494     }
35495    
35496
35497
35498     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
35499 };
35500
35501 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
35502     /*
35503      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
35504      */
35505     tabPosition : "top",
35506     /*
35507      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
35508      */
35509     currentTabWidth : 0,
35510     /*
35511      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
35512      */
35513     minTabWidth : 40,
35514     /*
35515      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
35516      */
35517     maxTabWidth : 250,
35518     /*
35519      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
35520      */
35521     preferredTabWidth : 175,
35522     /*
35523      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
35524      */
35525     resizeTabs : false,
35526     /*
35527      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
35528      */
35529     monitorResize : true,
35530     /*
35531      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
35532      */
35533     toolbar : false,
35534
35535     /**
35536      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
35537      * @param {String} id The id of the div to use <b>or create</b>
35538      * @param {String} text The text for the tab
35539      * @param {String} content (optional) Content to put in the TabPanelItem body
35540      * @param {Boolean} closable (optional) True to create a close icon on the tab
35541      * @return {Roo.TabPanelItem} The created TabPanelItem
35542      */
35543     addTab : function(id, text, content, closable, tpl)
35544     {
35545         var item = new Roo.bootstrap.panel.TabItem({
35546             panel: this,
35547             id : id,
35548             text : text,
35549             closable : closable,
35550             tpl : tpl
35551         });
35552         this.addTabItem(item);
35553         if(content){
35554             item.setContent(content);
35555         }
35556         return item;
35557     },
35558
35559     /**
35560      * Returns the {@link Roo.TabPanelItem} with the specified id/index
35561      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
35562      * @return {Roo.TabPanelItem}
35563      */
35564     getTab : function(id){
35565         return this.items[id];
35566     },
35567
35568     /**
35569      * Hides the {@link Roo.TabPanelItem} with the specified id/index
35570      * @param {String/Number} id The id or index of the TabPanelItem to hide.
35571      */
35572     hideTab : function(id){
35573         var t = this.items[id];
35574         if(!t.isHidden()){
35575            t.setHidden(true);
35576            this.hiddenCount++;
35577            this.autoSizeTabs();
35578         }
35579     },
35580
35581     /**
35582      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
35583      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
35584      */
35585     unhideTab : function(id){
35586         var t = this.items[id];
35587         if(t.isHidden()){
35588            t.setHidden(false);
35589            this.hiddenCount--;
35590            this.autoSizeTabs();
35591         }
35592     },
35593
35594     /**
35595      * Adds an existing {@link Roo.TabPanelItem}.
35596      * @param {Roo.TabPanelItem} item The TabPanelItem to add
35597      */
35598     addTabItem : function(item){
35599         this.items[item.id] = item;
35600         this.items.push(item);
35601       //  if(this.resizeTabs){
35602     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
35603   //         this.autoSizeTabs();
35604 //        }else{
35605 //            item.autoSize();
35606        // }
35607     },
35608
35609     /**
35610      * Removes a {@link Roo.TabPanelItem}.
35611      * @param {String/Number} id The id or index of the TabPanelItem to remove.
35612      */
35613     removeTab : function(id){
35614         var items = this.items;
35615         var tab = items[id];
35616         if(!tab) { return; }
35617         var index = items.indexOf(tab);
35618         if(this.active == tab && items.length > 1){
35619             var newTab = this.getNextAvailable(index);
35620             if(newTab) {
35621                 newTab.activate();
35622             }
35623         }
35624         this.stripEl.dom.removeChild(tab.pnode.dom);
35625         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
35626             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
35627         }
35628         items.splice(index, 1);
35629         delete this.items[tab.id];
35630         tab.fireEvent("close", tab);
35631         tab.purgeListeners();
35632         this.autoSizeTabs();
35633     },
35634
35635     getNextAvailable : function(start){
35636         var items = this.items;
35637         var index = start;
35638         // look for a next tab that will slide over to
35639         // replace the one being removed
35640         while(index < items.length){
35641             var item = items[++index];
35642             if(item && !item.isHidden()){
35643                 return item;
35644             }
35645         }
35646         // if one isn't found select the previous tab (on the left)
35647         index = start;
35648         while(index >= 0){
35649             var item = items[--index];
35650             if(item && !item.isHidden()){
35651                 return item;
35652             }
35653         }
35654         return null;
35655     },
35656
35657     /**
35658      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
35659      * @param {String/Number} id The id or index of the TabPanelItem to disable.
35660      */
35661     disableTab : function(id){
35662         var tab = this.items[id];
35663         if(tab && this.active != tab){
35664             tab.disable();
35665         }
35666     },
35667
35668     /**
35669      * Enables a {@link Roo.TabPanelItem} that is disabled.
35670      * @param {String/Number} id The id or index of the TabPanelItem to enable.
35671      */
35672     enableTab : function(id){
35673         var tab = this.items[id];
35674         tab.enable();
35675     },
35676
35677     /**
35678      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
35679      * @param {String/Number} id The id or index of the TabPanelItem to activate.
35680      * @return {Roo.TabPanelItem} The TabPanelItem.
35681      */
35682     activate : function(id){
35683         var tab = this.items[id];
35684         if(!tab){
35685             return null;
35686         }
35687         if(tab == this.active || tab.disabled){
35688             return tab;
35689         }
35690         var e = {};
35691         this.fireEvent("beforetabchange", this, e, tab);
35692         if(e.cancel !== true && !tab.disabled){
35693             if(this.active){
35694                 this.active.hide();
35695             }
35696             this.active = this.items[id];
35697             this.active.show();
35698             this.fireEvent("tabchange", this, this.active);
35699         }
35700         return tab;
35701     },
35702
35703     /**
35704      * Gets the active {@link Roo.TabPanelItem}.
35705      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
35706      */
35707     getActiveTab : function(){
35708         return this.active;
35709     },
35710
35711     /**
35712      * Updates the tab body element to fit the height of the container element
35713      * for overflow scrolling
35714      * @param {Number} targetHeight (optional) Override the starting height from the elements height
35715      */
35716     syncHeight : function(targetHeight){
35717         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35718         var bm = this.bodyEl.getMargins();
35719         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
35720         this.bodyEl.setHeight(newHeight);
35721         return newHeight;
35722     },
35723
35724     onResize : function(){
35725         if(this.monitorResize){
35726             this.autoSizeTabs();
35727         }
35728     },
35729
35730     /**
35731      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
35732      */
35733     beginUpdate : function(){
35734         this.updating = true;
35735     },
35736
35737     /**
35738      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
35739      */
35740     endUpdate : function(){
35741         this.updating = false;
35742         this.autoSizeTabs();
35743     },
35744
35745     /**
35746      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
35747      */
35748     autoSizeTabs : function(){
35749         var count = this.items.length;
35750         var vcount = count - this.hiddenCount;
35751         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
35752             return;
35753         }
35754         var w = Math.max(this.el.getWidth() - this.cpad, 10);
35755         var availWidth = Math.floor(w / vcount);
35756         var b = this.stripBody;
35757         if(b.getWidth() > w){
35758             var tabs = this.items;
35759             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
35760             if(availWidth < this.minTabWidth){
35761                 /*if(!this.sleft){    // incomplete scrolling code
35762                     this.createScrollButtons();
35763                 }
35764                 this.showScroll();
35765                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
35766             }
35767         }else{
35768             if(this.currentTabWidth < this.preferredTabWidth){
35769                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
35770             }
35771         }
35772     },
35773
35774     /**
35775      * Returns the number of tabs in this TabPanel.
35776      * @return {Number}
35777      */
35778      getCount : function(){
35779          return this.items.length;
35780      },
35781
35782     /**
35783      * Resizes all the tabs to the passed width
35784      * @param {Number} The new width
35785      */
35786     setTabWidth : function(width){
35787         this.currentTabWidth = width;
35788         for(var i = 0, len = this.items.length; i < len; i++) {
35789                 if(!this.items[i].isHidden()) {
35790                 this.items[i].setWidth(width);
35791             }
35792         }
35793     },
35794
35795     /**
35796      * Destroys this TabPanel
35797      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
35798      */
35799     destroy : function(removeEl){
35800         Roo.EventManager.removeResizeListener(this.onResize, this);
35801         for(var i = 0, len = this.items.length; i < len; i++){
35802             this.items[i].purgeListeners();
35803         }
35804         if(removeEl === true){
35805             this.el.update("");
35806             this.el.remove();
35807         }
35808     },
35809     
35810     createStrip : function(container)
35811     {
35812         var strip = document.createElement("nav");
35813         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
35814         container.appendChild(strip);
35815         return strip;
35816     },
35817     
35818     createStripList : function(strip)
35819     {
35820         // div wrapper for retard IE
35821         // returns the "tr" element.
35822         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
35823         //'<div class="x-tabs-strip-wrap">'+
35824           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
35825           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
35826         return strip.firstChild; //.firstChild.firstChild.firstChild;
35827     },
35828     createBody : function(container)
35829     {
35830         var body = document.createElement("div");
35831         Roo.id(body, "tab-body");
35832         //Roo.fly(body).addClass("x-tabs-body");
35833         Roo.fly(body).addClass("tab-content");
35834         container.appendChild(body);
35835         return body;
35836     },
35837     createItemBody :function(bodyEl, id){
35838         var body = Roo.getDom(id);
35839         if(!body){
35840             body = document.createElement("div");
35841             body.id = id;
35842         }
35843         //Roo.fly(body).addClass("x-tabs-item-body");
35844         Roo.fly(body).addClass("tab-pane");
35845          bodyEl.insertBefore(body, bodyEl.firstChild);
35846         return body;
35847     },
35848     /** @private */
35849     createStripElements :  function(stripEl, text, closable, tpl)
35850     {
35851         var td = document.createElement("li"); // was td..
35852         
35853         
35854         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
35855         
35856         
35857         stripEl.appendChild(td);
35858         /*if(closable){
35859             td.className = "x-tabs-closable";
35860             if(!this.closeTpl){
35861                 this.closeTpl = new Roo.Template(
35862                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35863                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
35864                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
35865                 );
35866             }
35867             var el = this.closeTpl.overwrite(td, {"text": text});
35868             var close = el.getElementsByTagName("div")[0];
35869             var inner = el.getElementsByTagName("em")[0];
35870             return {"el": el, "close": close, "inner": inner};
35871         } else {
35872         */
35873         // not sure what this is..
35874 //            if(!this.tabTpl){
35875                 //this.tabTpl = new Roo.Template(
35876                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35877                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
35878                 //);
35879 //                this.tabTpl = new Roo.Template(
35880 //                   '<a href="#">' +
35881 //                   '<span unselectable="on"' +
35882 //                            (this.disableTooltips ? '' : ' title="{text}"') +
35883 //                            ' >{text}</span></a>'
35884 //                );
35885 //                
35886 //            }
35887
35888
35889             var template = tpl || this.tabTpl || false;
35890             
35891             if(!template){
35892                 
35893                 template = new Roo.Template(
35894                    '<a href="#">' +
35895                    '<span unselectable="on"' +
35896                             (this.disableTooltips ? '' : ' title="{text}"') +
35897                             ' >{text}</span></a>'
35898                 );
35899             }
35900             
35901             switch (typeof(template)) {
35902                 case 'object' :
35903                     break;
35904                 case 'string' :
35905                     template = new Roo.Template(template);
35906                     break;
35907                 default :
35908                     break;
35909             }
35910             
35911             var el = template.overwrite(td, {"text": text});
35912             
35913             var inner = el.getElementsByTagName("span")[0];
35914             
35915             return {"el": el, "inner": inner};
35916             
35917     }
35918         
35919     
35920 });
35921
35922 /**
35923  * @class Roo.TabPanelItem
35924  * @extends Roo.util.Observable
35925  * Represents an individual item (tab plus body) in a TabPanel.
35926  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
35927  * @param {String} id The id of this TabPanelItem
35928  * @param {String} text The text for the tab of this TabPanelItem
35929  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
35930  */
35931 Roo.bootstrap.panel.TabItem = function(config){
35932     /**
35933      * The {@link Roo.TabPanel} this TabPanelItem belongs to
35934      * @type Roo.TabPanel
35935      */
35936     this.tabPanel = config.panel;
35937     /**
35938      * The id for this TabPanelItem
35939      * @type String
35940      */
35941     this.id = config.id;
35942     /** @private */
35943     this.disabled = false;
35944     /** @private */
35945     this.text = config.text;
35946     /** @private */
35947     this.loaded = false;
35948     this.closable = config.closable;
35949
35950     /**
35951      * The body element for this TabPanelItem.
35952      * @type Roo.Element
35953      */
35954     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
35955     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
35956     this.bodyEl.setStyle("display", "block");
35957     this.bodyEl.setStyle("zoom", "1");
35958     //this.hideAction();
35959
35960     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
35961     /** @private */
35962     this.el = Roo.get(els.el);
35963     this.inner = Roo.get(els.inner, true);
35964     this.textEl = Roo.get(this.el.dom.firstChild, true);
35965     this.pnode = Roo.get(els.el.parentNode, true);
35966     this.el.on("mousedown", this.onTabMouseDown, this);
35967     this.el.on("click", this.onTabClick, this);
35968     /** @private */
35969     if(config.closable){
35970         var c = Roo.get(els.close, true);
35971         c.dom.title = this.closeText;
35972         c.addClassOnOver("close-over");
35973         c.on("click", this.closeClick, this);
35974      }
35975
35976     this.addEvents({
35977          /**
35978          * @event activate
35979          * Fires when this tab becomes the active tab.
35980          * @param {Roo.TabPanel} tabPanel The parent TabPanel
35981          * @param {Roo.TabPanelItem} this
35982          */
35983         "activate": true,
35984         /**
35985          * @event beforeclose
35986          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
35987          * @param {Roo.TabPanelItem} this
35988          * @param {Object} e Set cancel to true on this object to cancel the close.
35989          */
35990         "beforeclose": true,
35991         /**
35992          * @event close
35993          * Fires when this tab is closed.
35994          * @param {Roo.TabPanelItem} this
35995          */
35996          "close": true,
35997         /**
35998          * @event deactivate
35999          * Fires when this tab is no longer the active tab.
36000          * @param {Roo.TabPanel} tabPanel The parent TabPanel
36001          * @param {Roo.TabPanelItem} this
36002          */
36003          "deactivate" : true
36004     });
36005     this.hidden = false;
36006
36007     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
36008 };
36009
36010 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
36011            {
36012     purgeListeners : function(){
36013        Roo.util.Observable.prototype.purgeListeners.call(this);
36014        this.el.removeAllListeners();
36015     },
36016     /**
36017      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
36018      */
36019     show : function(){
36020         this.pnode.addClass("active");
36021         this.showAction();
36022         if(Roo.isOpera){
36023             this.tabPanel.stripWrap.repaint();
36024         }
36025         this.fireEvent("activate", this.tabPanel, this);
36026     },
36027
36028     /**
36029      * Returns true if this tab is the active tab.
36030      * @return {Boolean}
36031      */
36032     isActive : function(){
36033         return this.tabPanel.getActiveTab() == this;
36034     },
36035
36036     /**
36037      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
36038      */
36039     hide : function(){
36040         this.pnode.removeClass("active");
36041         this.hideAction();
36042         this.fireEvent("deactivate", this.tabPanel, this);
36043     },
36044
36045     hideAction : function(){
36046         this.bodyEl.hide();
36047         this.bodyEl.setStyle("position", "absolute");
36048         this.bodyEl.setLeft("-20000px");
36049         this.bodyEl.setTop("-20000px");
36050     },
36051
36052     showAction : function(){
36053         this.bodyEl.setStyle("position", "relative");
36054         this.bodyEl.setTop("");
36055         this.bodyEl.setLeft("");
36056         this.bodyEl.show();
36057     },
36058
36059     /**
36060      * Set the tooltip for the tab.
36061      * @param {String} tooltip The tab's tooltip
36062      */
36063     setTooltip : function(text){
36064         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
36065             this.textEl.dom.qtip = text;
36066             this.textEl.dom.removeAttribute('title');
36067         }else{
36068             this.textEl.dom.title = text;
36069         }
36070     },
36071
36072     onTabClick : function(e){
36073         e.preventDefault();
36074         this.tabPanel.activate(this.id);
36075     },
36076
36077     onTabMouseDown : function(e){
36078         e.preventDefault();
36079         this.tabPanel.activate(this.id);
36080     },
36081 /*
36082     getWidth : function(){
36083         return this.inner.getWidth();
36084     },
36085
36086     setWidth : function(width){
36087         var iwidth = width - this.pnode.getPadding("lr");
36088         this.inner.setWidth(iwidth);
36089         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
36090         this.pnode.setWidth(width);
36091     },
36092 */
36093     /**
36094      * Show or hide the tab
36095      * @param {Boolean} hidden True to hide or false to show.
36096      */
36097     setHidden : function(hidden){
36098         this.hidden = hidden;
36099         this.pnode.setStyle("display", hidden ? "none" : "");
36100     },
36101
36102     /**
36103      * Returns true if this tab is "hidden"
36104      * @return {Boolean}
36105      */
36106     isHidden : function(){
36107         return this.hidden;
36108     },
36109
36110     /**
36111      * Returns the text for this tab
36112      * @return {String}
36113      */
36114     getText : function(){
36115         return this.text;
36116     },
36117     /*
36118     autoSize : function(){
36119         //this.el.beginMeasure();
36120         this.textEl.setWidth(1);
36121         /*
36122          *  #2804 [new] Tabs in Roojs
36123          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
36124          */
36125         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
36126         //this.el.endMeasure();
36127     //},
36128
36129     /**
36130      * Sets the text for the tab (Note: this also sets the tooltip text)
36131      * @param {String} text The tab's text and tooltip
36132      */
36133     setText : function(text){
36134         this.text = text;
36135         this.textEl.update(text);
36136         this.setTooltip(text);
36137         //if(!this.tabPanel.resizeTabs){
36138         //    this.autoSize();
36139         //}
36140     },
36141     /**
36142      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
36143      */
36144     activate : function(){
36145         this.tabPanel.activate(this.id);
36146     },
36147
36148     /**
36149      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
36150      */
36151     disable : function(){
36152         if(this.tabPanel.active != this){
36153             this.disabled = true;
36154             this.pnode.addClass("disabled");
36155         }
36156     },
36157
36158     /**
36159      * Enables this TabPanelItem if it was previously disabled.
36160      */
36161     enable : function(){
36162         this.disabled = false;
36163         this.pnode.removeClass("disabled");
36164     },
36165
36166     /**
36167      * Sets the content for this TabPanelItem.
36168      * @param {String} content The content
36169      * @param {Boolean} loadScripts true to look for and load scripts
36170      */
36171     setContent : function(content, loadScripts){
36172         this.bodyEl.update(content, loadScripts);
36173     },
36174
36175     /**
36176      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
36177      * @return {Roo.UpdateManager} The UpdateManager
36178      */
36179     getUpdateManager : function(){
36180         return this.bodyEl.getUpdateManager();
36181     },
36182
36183     /**
36184      * Set a URL to be used to load the content for this TabPanelItem.
36185      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
36186      * @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)
36187      * @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)
36188      * @return {Roo.UpdateManager} The UpdateManager
36189      */
36190     setUrl : function(url, params, loadOnce){
36191         if(this.refreshDelegate){
36192             this.un('activate', this.refreshDelegate);
36193         }
36194         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36195         this.on("activate", this.refreshDelegate);
36196         return this.bodyEl.getUpdateManager();
36197     },
36198
36199     /** @private */
36200     _handleRefresh : function(url, params, loadOnce){
36201         if(!loadOnce || !this.loaded){
36202             var updater = this.bodyEl.getUpdateManager();
36203             updater.update(url, params, this._setLoaded.createDelegate(this));
36204         }
36205     },
36206
36207     /**
36208      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
36209      *   Will fail silently if the setUrl method has not been called.
36210      *   This does not activate the panel, just updates its content.
36211      */
36212     refresh : function(){
36213         if(this.refreshDelegate){
36214            this.loaded = false;
36215            this.refreshDelegate();
36216         }
36217     },
36218
36219     /** @private */
36220     _setLoaded : function(){
36221         this.loaded = true;
36222     },
36223
36224     /** @private */
36225     closeClick : function(e){
36226         var o = {};
36227         e.stopEvent();
36228         this.fireEvent("beforeclose", this, o);
36229         if(o.cancel !== true){
36230             this.tabPanel.removeTab(this.id);
36231         }
36232     },
36233     /**
36234      * The text displayed in the tooltip for the close icon.
36235      * @type String
36236      */
36237     closeText : "Close this tab"
36238 });