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                 
3083                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3084                 //var pf = progressEl.dom.firstChild;
3085                 //if (pf) {
3086                     //pp = Roo.get(pf.firstChild);
3087                     //pp.setHeight(pf.offsetHeight);
3088                 //}
3089                 
3090             }
3091             return dlg;
3092         },
3093
3094         /**
3095          * Updates the message box body text
3096          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3097          * the XHTML-compliant non-breaking space character '&amp;#160;')
3098          * @return {Roo.MessageBox} This message box
3099          */
3100         updateText : function(text)
3101         {
3102             if(!dlg.isVisible() && !opt.width){
3103                 dlg.dialogEl.setWidth(this.maxWidth);
3104                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3105             }
3106             msgEl.innerHTML = text || '&#160;';
3107       
3108             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3109             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3110             var w = Math.max(
3111                     Math.min(opt.width || cw , this.maxWidth), 
3112                     Math.max(opt.minWidth || this.minWidth, bwidth)
3113             );
3114             if(opt.prompt){
3115                 activeTextEl.setWidth(w);
3116             }
3117             if(dlg.isVisible()){
3118                 dlg.fixedcenter = false;
3119             }
3120             // to big, make it scroll. = But as usual stupid IE does not support
3121             // !important..
3122             
3123             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3124                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3125                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3126             } else {
3127                 bodyEl.dom.style.height = '';
3128                 bodyEl.dom.style.overflowY = '';
3129             }
3130             if (cw > w) {
3131                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3132             } else {
3133                 bodyEl.dom.style.overflowX = '';
3134             }
3135             
3136             dlg.setContentSize(w, bodyEl.getHeight());
3137             if(dlg.isVisible()){
3138                 dlg.fixedcenter = true;
3139             }
3140             return this;
3141         },
3142
3143         /**
3144          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3145          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3146          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3147          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3148          * @return {Roo.MessageBox} This message box
3149          */
3150         updateProgress : function(value, text){
3151             if(text){
3152                 this.updateText(text);
3153             }
3154             if (pp) { // weird bug on my firefox - for some reason this is not defined
3155                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3156             }
3157             return this;
3158         },        
3159
3160         /**
3161          * Returns true if the message box is currently displayed
3162          * @return {Boolean} True if the message box is visible, else false
3163          */
3164         isVisible : function(){
3165             return dlg && dlg.isVisible();  
3166         },
3167
3168         /**
3169          * Hides the message box if it is displayed
3170          */
3171         hide : function(){
3172             if(this.isVisible()){
3173                 dlg.hide();
3174             }  
3175         },
3176
3177         /**
3178          * Displays a new message box, or reinitializes an existing message box, based on the config options
3179          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3180          * The following config object properties are supported:
3181          * <pre>
3182 Property    Type             Description
3183 ----------  ---------------  ------------------------------------------------------------------------------------
3184 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3185                                    closes (defaults to undefined)
3186 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3187                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3188 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3189                                    progress and wait dialogs will ignore this property and always hide the
3190                                    close button as they can only be closed programmatically.
3191 cls               String           A custom CSS class to apply to the message box element
3192 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3193                                    displayed (defaults to 75)
3194 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3195                                    function will be btn (the name of the button that was clicked, if applicable,
3196                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3197                                    Progress and wait dialogs will ignore this option since they do not respond to
3198                                    user actions and can only be closed programmatically, so any required function
3199                                    should be called by the same code after it closes the dialog.
3200 icon              String           A CSS class that provides a background image to be used as an icon for
3201                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3202 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3203 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3204 modal             Boolean          False to allow user interaction with the page while the message box is
3205                                    displayed (defaults to true)
3206 msg               String           A string that will replace the existing message box body text (defaults
3207                                    to the XHTML-compliant non-breaking space character '&#160;')
3208 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3209 progress          Boolean          True to display a progress bar (defaults to false)
3210 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3211 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3212 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3213 title             String           The title text
3214 value             String           The string value to set into the active textbox element if displayed
3215 wait              Boolean          True to display a progress bar (defaults to false)
3216 width             Number           The width of the dialog in pixels
3217 </pre>
3218          *
3219          * Example usage:
3220          * <pre><code>
3221 Roo.Msg.show({
3222    title: 'Address',
3223    msg: 'Please enter your address:',
3224    width: 300,
3225    buttons: Roo.MessageBox.OKCANCEL,
3226    multiline: true,
3227    fn: saveAddress,
3228    animEl: 'addAddressBtn'
3229 });
3230 </code></pre>
3231          * @param {Object} config Configuration options
3232          * @return {Roo.MessageBox} This message box
3233          */
3234         show : function(options)
3235         {
3236             
3237             // this causes nightmares if you show one dialog after another
3238             // especially on callbacks..
3239              
3240             if(this.isVisible()){
3241                 
3242                 this.hide();
3243                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3244                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3245                 Roo.log("New Dialog Message:" +  options.msg )
3246                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3247                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3248                 
3249             }
3250             var d = this.getDialog();
3251             opt = options;
3252             d.setTitle(opt.title || "&#160;");
3253             d.closeEl.setDisplayed(opt.closable !== false);
3254             activeTextEl = textboxEl;
3255             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3256             if(opt.prompt){
3257                 if(opt.multiline){
3258                     textboxEl.hide();
3259                     textareaEl.show();
3260                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3261                         opt.multiline : this.defaultTextHeight);
3262                     activeTextEl = textareaEl;
3263                 }else{
3264                     textboxEl.show();
3265                     textareaEl.hide();
3266                 }
3267             }else{
3268                 textboxEl.hide();
3269                 textareaEl.hide();
3270             }
3271             progressEl.setDisplayed(opt.progress === true);
3272             this.updateProgress(0);
3273             activeTextEl.dom.value = opt.value || "";
3274             if(opt.prompt){
3275                 dlg.setDefaultButton(activeTextEl);
3276             }else{
3277                 var bs = opt.buttons;
3278                 var db = null;
3279                 if(bs && bs.ok){
3280                     db = buttons["ok"];
3281                 }else if(bs && bs.yes){
3282                     db = buttons["yes"];
3283                 }
3284                 dlg.setDefaultButton(db);
3285             }
3286             bwidth = updateButtons(opt.buttons);
3287             this.updateText(opt.msg);
3288             if(opt.cls){
3289                 d.el.addClass(opt.cls);
3290             }
3291             d.proxyDrag = opt.proxyDrag === true;
3292             d.modal = opt.modal !== false;
3293             d.mask = opt.modal !== false ? mask : false;
3294             if(!d.isVisible()){
3295                 // force it to the end of the z-index stack so it gets a cursor in FF
3296                 document.body.appendChild(dlg.el.dom);
3297                 d.animateTarget = null;
3298                 d.show(options.animEl);
3299             }
3300             return this;
3301         },
3302
3303         /**
3304          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3305          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3306          * and closing the message box when the process is complete.
3307          * @param {String} title The title bar text
3308          * @param {String} msg The message box body text
3309          * @return {Roo.MessageBox} This message box
3310          */
3311         progress : function(title, msg){
3312             this.show({
3313                 title : title,
3314                 msg : msg,
3315                 buttons: false,
3316                 progress:true,
3317                 closable:false,
3318                 minWidth: this.minProgressWidth,
3319                 modal : true
3320             });
3321             return this;
3322         },
3323
3324         /**
3325          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3326          * If a callback function is passed it will be called after the user clicks the button, and the
3327          * id of the button that was clicked will be passed as the only parameter to the callback
3328          * (could also be the top-right close button).
3329          * @param {String} title The title bar text
3330          * @param {String} msg The message box body text
3331          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3332          * @param {Object} scope (optional) The scope of the callback function
3333          * @return {Roo.MessageBox} This message box
3334          */
3335         alert : function(title, msg, fn, scope)
3336         {
3337             this.show({
3338                 title : title,
3339                 msg : msg,
3340                 buttons: this.OK,
3341                 fn: fn,
3342                 closable : false,
3343                 scope : scope,
3344                 modal : true
3345             });
3346             return this;
3347         },
3348
3349         /**
3350          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3351          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3352          * You are responsible for closing the message box when the process is complete.
3353          * @param {String} msg The message box body text
3354          * @param {String} title (optional) The title bar text
3355          * @return {Roo.MessageBox} This message box
3356          */
3357         wait : function(msg, title){
3358             this.show({
3359                 title : title,
3360                 msg : msg,
3361                 buttons: false,
3362                 closable:false,
3363                 progress:true,
3364                 modal:true,
3365                 width:300,
3366                 wait:true
3367             });
3368             waitTimer = Roo.TaskMgr.start({
3369                 run: function(i){
3370                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3371                 },
3372                 interval: 1000
3373             });
3374             return this;
3375         },
3376
3377         /**
3378          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3379          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3380          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3381          * @param {String} title The title bar text
3382          * @param {String} msg The message box body text
3383          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3384          * @param {Object} scope (optional) The scope of the callback function
3385          * @return {Roo.MessageBox} This message box
3386          */
3387         confirm : function(title, msg, fn, scope){
3388             this.show({
3389                 title : title,
3390                 msg : msg,
3391                 buttons: this.YESNO,
3392                 fn: fn,
3393                 scope : scope,
3394                 modal : true
3395             });
3396             return this;
3397         },
3398
3399         /**
3400          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3401          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3402          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3403          * (could also be the top-right close button) and the text that was entered will be passed as the two
3404          * parameters to the callback.
3405          * @param {String} title The title bar text
3406          * @param {String} msg The message box body text
3407          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3408          * @param {Object} scope (optional) The scope of the callback function
3409          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3410          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3411          * @return {Roo.MessageBox} This message box
3412          */
3413         prompt : function(title, msg, fn, scope, multiline){
3414             this.show({
3415                 title : title,
3416                 msg : msg,
3417                 buttons: this.OKCANCEL,
3418                 fn: fn,
3419                 minWidth:250,
3420                 scope : scope,
3421                 prompt:true,
3422                 multiline: multiline,
3423                 modal : true
3424             });
3425             return this;
3426         },
3427
3428         /**
3429          * Button config that displays a single OK button
3430          * @type Object
3431          */
3432         OK : {ok:true},
3433         /**
3434          * Button config that displays Yes and No buttons
3435          * @type Object
3436          */
3437         YESNO : {yes:true, no:true},
3438         /**
3439          * Button config that displays OK and Cancel buttons
3440          * @type Object
3441          */
3442         OKCANCEL : {ok:true, cancel:true},
3443         /**
3444          * Button config that displays Yes, No and Cancel buttons
3445          * @type Object
3446          */
3447         YESNOCANCEL : {yes:true, no:true, cancel:true},
3448
3449         /**
3450          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3451          * @type Number
3452          */
3453         defaultTextHeight : 75,
3454         /**
3455          * The maximum width in pixels of the message box (defaults to 600)
3456          * @type Number
3457          */
3458         maxWidth : 600,
3459         /**
3460          * The minimum width in pixels of the message box (defaults to 100)
3461          * @type Number
3462          */
3463         minWidth : 100,
3464         /**
3465          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3466          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3467          * @type Number
3468          */
3469         minProgressWidth : 250,
3470         /**
3471          * An object containing the default button text strings that can be overriden for localized language support.
3472          * Supported properties are: ok, cancel, yes and no.
3473          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3474          * @type Object
3475          */
3476         buttonText : {
3477             ok : "OK",
3478             cancel : "Cancel",
3479             yes : "Yes",
3480             no : "No"
3481         }
3482     };
3483 }();
3484
3485 /**
3486  * Shorthand for {@link Roo.MessageBox}
3487  */
3488 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3489 Roo.Msg = Roo.Msg || Roo.MessageBox;
3490 /*
3491  * - LGPL
3492  *
3493  * navbar
3494  * 
3495  */
3496
3497 /**
3498  * @class Roo.bootstrap.Navbar
3499  * @extends Roo.bootstrap.Component
3500  * Bootstrap Navbar class
3501
3502  * @constructor
3503  * Create a new Navbar
3504  * @param {Object} config The config object
3505  */
3506
3507
3508 Roo.bootstrap.Navbar = function(config){
3509     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3510     this.addEvents({
3511         // raw events
3512         /**
3513          * @event beforetoggle
3514          * Fire before toggle the menu
3515          * @param {Roo.EventObject} e
3516          */
3517         "beforetoggle" : true
3518     });
3519 };
3520
3521 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3522     
3523     
3524    
3525     // private
3526     navItems : false,
3527     loadMask : false,
3528     
3529     
3530     getAutoCreate : function(){
3531         
3532         
3533         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3534         
3535     },
3536     
3537     initEvents :function ()
3538     {
3539         //Roo.log(this.el.select('.navbar-toggle',true));
3540         this.el.select('.navbar-toggle',true).on('click', function() {
3541             if(this.fireEvent('beforetoggle', this) !== false){
3542                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3543             }
3544             
3545         }, this);
3546         
3547         var mark = {
3548             tag: "div",
3549             cls:"x-dlg-mask"
3550         };
3551         
3552         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3553         
3554         var size = this.el.getSize();
3555         this.maskEl.setSize(size.width, size.height);
3556         this.maskEl.enableDisplayMode("block");
3557         this.maskEl.hide();
3558         
3559         if(this.loadMask){
3560             this.maskEl.show();
3561         }
3562     },
3563     
3564     
3565     getChildContainer : function()
3566     {
3567         if (this.el.select('.collapse').getCount()) {
3568             return this.el.select('.collapse',true).first();
3569         }
3570         
3571         return this.el;
3572     },
3573     
3574     mask : function()
3575     {
3576         this.maskEl.show();
3577     },
3578     
3579     unmask : function()
3580     {
3581         this.maskEl.hide();
3582     } 
3583     
3584     
3585     
3586     
3587 });
3588
3589
3590
3591  
3592
3593  /*
3594  * - LGPL
3595  *
3596  * navbar
3597  * 
3598  */
3599
3600 /**
3601  * @class Roo.bootstrap.NavSimplebar
3602  * @extends Roo.bootstrap.Navbar
3603  * Bootstrap Sidebar class
3604  *
3605  * @cfg {Boolean} inverse is inverted color
3606  * 
3607  * @cfg {String} type (nav | pills | tabs)
3608  * @cfg {Boolean} arrangement stacked | justified
3609  * @cfg {String} align (left | right) alignment
3610  * 
3611  * @cfg {Boolean} main (true|false) main nav bar? default false
3612  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3613  * 
3614  * @cfg {String} tag (header|footer|nav|div) default is nav 
3615
3616  * 
3617  * 
3618  * 
3619  * @constructor
3620  * Create a new Sidebar
3621  * @param {Object} config The config object
3622  */
3623
3624
3625 Roo.bootstrap.NavSimplebar = function(config){
3626     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3627 };
3628
3629 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3630     
3631     inverse: false,
3632     
3633     type: false,
3634     arrangement: '',
3635     align : false,
3636     
3637     
3638     
3639     main : false,
3640     
3641     
3642     tag : false,
3643     
3644     
3645     getAutoCreate : function(){
3646         
3647         
3648         var cfg = {
3649             tag : this.tag || 'div',
3650             cls : 'navbar'
3651         };
3652           
3653         
3654         cfg.cn = [
3655             {
3656                 cls: 'nav',
3657                 tag : 'ul'
3658             }
3659         ];
3660         
3661          
3662         this.type = this.type || 'nav';
3663         if (['tabs','pills'].indexOf(this.type)!==-1) {
3664             cfg.cn[0].cls += ' nav-' + this.type
3665         
3666         
3667         } else {
3668             if (this.type!=='nav') {
3669                 Roo.log('nav type must be nav/tabs/pills')
3670             }
3671             cfg.cn[0].cls += ' navbar-nav'
3672         }
3673         
3674         
3675         
3676         
3677         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3678             cfg.cn[0].cls += ' nav-' + this.arrangement;
3679         }
3680         
3681         
3682         if (this.align === 'right') {
3683             cfg.cn[0].cls += ' navbar-right';
3684         }
3685         
3686         if (this.inverse) {
3687             cfg.cls += ' navbar-inverse';
3688             
3689         }
3690         
3691         
3692         return cfg;
3693     
3694         
3695     }
3696     
3697     
3698     
3699 });
3700
3701
3702
3703  
3704
3705  
3706        /*
3707  * - LGPL
3708  *
3709  * navbar
3710  * 
3711  */
3712
3713 /**
3714  * @class Roo.bootstrap.NavHeaderbar
3715  * @extends Roo.bootstrap.NavSimplebar
3716  * Bootstrap Sidebar class
3717  *
3718  * @cfg {String} brand what is brand
3719  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3720  * @cfg {String} brand_href href of the brand
3721  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3722  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3723  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3724  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3725  * 
3726  * @constructor
3727  * Create a new Sidebar
3728  * @param {Object} config The config object
3729  */
3730
3731
3732 Roo.bootstrap.NavHeaderbar = function(config){
3733     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3734       
3735 };
3736
3737 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3738     
3739     position: '',
3740     brand: '',
3741     brand_href: false,
3742     srButton : true,
3743     autohide : false,
3744     desktopCenter : false,
3745    
3746     
3747     getAutoCreate : function(){
3748         
3749         var   cfg = {
3750             tag: this.nav || 'nav',
3751             cls: 'navbar',
3752             role: 'navigation',
3753             cn: []
3754         };
3755         
3756         var cn = cfg.cn;
3757         if (this.desktopCenter) {
3758             cn.push({cls : 'container', cn : []});
3759             cn = cn[0].cn;
3760         }
3761         
3762         if(this.srButton){
3763             cn.push({
3764                 tag: 'div',
3765                 cls: 'navbar-header',
3766                 cn: [
3767                     {
3768                         tag: 'button',
3769                         type: 'button',
3770                         cls: 'navbar-toggle',
3771                         'data-toggle': 'collapse',
3772                         cn: [
3773                             {
3774                                 tag: 'span',
3775                                 cls: 'sr-only',
3776                                 html: 'Toggle navigation'
3777                             },
3778                             {
3779                                 tag: 'span',
3780                                 cls: 'icon-bar'
3781                             },
3782                             {
3783                                 tag: 'span',
3784                                 cls: 'icon-bar'
3785                             },
3786                             {
3787                                 tag: 'span',
3788                                 cls: 'icon-bar'
3789                             }
3790                         ]
3791                     }
3792                 ]
3793             });
3794         }
3795         
3796         cn.push({
3797             tag: 'div',
3798             cls: 'collapse navbar-collapse',
3799             cn : []
3800         });
3801         
3802         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3803         
3804         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3805             cfg.cls += ' navbar-' + this.position;
3806             
3807             // tag can override this..
3808             
3809             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3810         }
3811         
3812         if (this.brand !== '') {
3813             cn[0].cn.push({
3814                 tag: 'a',
3815                 href: this.brand_href ? this.brand_href : '#',
3816                 cls: 'navbar-brand',
3817                 cn: [
3818                 this.brand
3819                 ]
3820             });
3821         }
3822         
3823         if(this.main){
3824             cfg.cls += ' main-nav';
3825         }
3826         
3827         
3828         return cfg;
3829
3830         
3831     },
3832     getHeaderChildContainer : function()
3833     {
3834         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3835             return this.el.select('.navbar-header',true).first();
3836         }
3837         
3838         return this.getChildContainer();
3839     },
3840     
3841     
3842     initEvents : function()
3843     {
3844         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3845         
3846         if (this.autohide) {
3847             
3848             var prevScroll = 0;
3849             var ft = this.el;
3850             
3851             Roo.get(document).on('scroll',function(e) {
3852                 var ns = Roo.get(document).getScroll().top;
3853                 var os = prevScroll;
3854                 prevScroll = ns;
3855                 
3856                 if(ns > os){
3857                     ft.removeClass('slideDown');
3858                     ft.addClass('slideUp');
3859                     return;
3860                 }
3861                 ft.removeClass('slideUp');
3862                 ft.addClass('slideDown');
3863                  
3864               
3865           },this);
3866         }
3867     }    
3868     
3869 });
3870
3871
3872
3873  
3874
3875  /*
3876  * - LGPL
3877  *
3878  * navbar
3879  * 
3880  */
3881
3882 /**
3883  * @class Roo.bootstrap.NavSidebar
3884  * @extends Roo.bootstrap.Navbar
3885  * Bootstrap Sidebar class
3886  * 
3887  * @constructor
3888  * Create a new Sidebar
3889  * @param {Object} config The config object
3890  */
3891
3892
3893 Roo.bootstrap.NavSidebar = function(config){
3894     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3895 };
3896
3897 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3898     
3899     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3900     
3901     getAutoCreate : function(){
3902         
3903         
3904         return  {
3905             tag: 'div',
3906             cls: 'sidebar sidebar-nav'
3907         };
3908     
3909         
3910     }
3911     
3912     
3913     
3914 });
3915
3916
3917
3918  
3919
3920  /*
3921  * - LGPL
3922  *
3923  * nav group
3924  * 
3925  */
3926
3927 /**
3928  * @class Roo.bootstrap.NavGroup
3929  * @extends Roo.bootstrap.Component
3930  * Bootstrap NavGroup class
3931  * @cfg {String} align (left|right)
3932  * @cfg {Boolean} inverse
3933  * @cfg {String} type (nav|pills|tab) default nav
3934  * @cfg {String} navId - reference Id for navbar.
3935
3936  * 
3937  * @constructor
3938  * Create a new nav group
3939  * @param {Object} config The config object
3940  */
3941
3942 Roo.bootstrap.NavGroup = function(config){
3943     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3944     this.navItems = [];
3945    
3946     Roo.bootstrap.NavGroup.register(this);
3947      this.addEvents({
3948         /**
3949              * @event changed
3950              * Fires when the active item changes
3951              * @param {Roo.bootstrap.NavGroup} this
3952              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3953              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3954          */
3955         'changed': true
3956      });
3957     
3958 };
3959
3960 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3961     
3962     align: '',
3963     inverse: false,
3964     form: false,
3965     type: 'nav',
3966     navId : '',
3967     // private
3968     
3969     navItems : false, 
3970     
3971     getAutoCreate : function()
3972     {
3973         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3974         
3975         cfg = {
3976             tag : 'ul',
3977             cls: 'nav' 
3978         };
3979         
3980         if (['tabs','pills'].indexOf(this.type)!==-1) {
3981             cfg.cls += ' nav-' + this.type
3982         } else {
3983             if (this.type!=='nav') {
3984                 Roo.log('nav type must be nav/tabs/pills')
3985             }
3986             cfg.cls += ' navbar-nav'
3987         }
3988         
3989         if (this.parent().sidebar) {
3990             cfg = {
3991                 tag: 'ul',
3992                 cls: 'dashboard-menu sidebar-menu'
3993             };
3994             
3995             return cfg;
3996         }
3997         
3998         if (this.form === true) {
3999             cfg = {
4000                 tag: 'form',
4001                 cls: 'navbar-form'
4002             };
4003             
4004             if (this.align === 'right') {
4005                 cfg.cls += ' navbar-right';
4006             } else {
4007                 cfg.cls += ' navbar-left';
4008             }
4009         }
4010         
4011         if (this.align === 'right') {
4012             cfg.cls += ' navbar-right';
4013         }
4014         
4015         if (this.inverse) {
4016             cfg.cls += ' navbar-inverse';
4017             
4018         }
4019         
4020         
4021         return cfg;
4022     },
4023     /**
4024     * sets the active Navigation item
4025     * @param {Roo.bootstrap.NavItem} the new current navitem
4026     */
4027     setActiveItem : function(item)
4028     {
4029         var prev = false;
4030         Roo.each(this.navItems, function(v){
4031             if (v == item) {
4032                 return ;
4033             }
4034             if (v.isActive()) {
4035                 v.setActive(false, true);
4036                 prev = v;
4037                 
4038             }
4039             
4040         });
4041
4042         item.setActive(true, true);
4043         this.fireEvent('changed', this, item, prev);
4044         
4045         
4046     },
4047     /**
4048     * gets the active Navigation item
4049     * @return {Roo.bootstrap.NavItem} the current navitem
4050     */
4051     getActive : function()
4052     {
4053         
4054         var prev = false;
4055         Roo.each(this.navItems, function(v){
4056             
4057             if (v.isActive()) {
4058                 prev = v;
4059                 
4060             }
4061             
4062         });
4063         return prev;
4064     },
4065     
4066     indexOfNav : function()
4067     {
4068         
4069         var prev = false;
4070         Roo.each(this.navItems, function(v,i){
4071             
4072             if (v.isActive()) {
4073                 prev = i;
4074                 
4075             }
4076             
4077         });
4078         return prev;
4079     },
4080     /**
4081     * adds a Navigation item
4082     * @param {Roo.bootstrap.NavItem} the navitem to add
4083     */
4084     addItem : function(cfg)
4085     {
4086         var cn = new Roo.bootstrap.NavItem(cfg);
4087         this.register(cn);
4088         cn.parentId = this.id;
4089         cn.onRender(this.el, null);
4090         return cn;
4091     },
4092     /**
4093     * register a Navigation item
4094     * @param {Roo.bootstrap.NavItem} the navitem to add
4095     */
4096     register : function(item)
4097     {
4098         this.navItems.push( item);
4099         item.navId = this.navId;
4100     
4101     },
4102     
4103     /**
4104     * clear all the Navigation item
4105     */
4106    
4107     clearAll : function()
4108     {
4109         this.navItems = [];
4110         this.el.dom.innerHTML = '';
4111     },
4112     
4113     getNavItem: function(tabId)
4114     {
4115         var ret = false;
4116         Roo.each(this.navItems, function(e) {
4117             if (e.tabId == tabId) {
4118                ret =  e;
4119                return false;
4120             }
4121             return true;
4122             
4123         });
4124         return ret;
4125     },
4126     
4127     setActiveNext : function()
4128     {
4129         var i = this.indexOfNav(this.getActive());
4130         if (i > this.navItems.length) {
4131             return;
4132         }
4133         this.setActiveItem(this.navItems[i+1]);
4134     },
4135     setActivePrev : function()
4136     {
4137         var i = this.indexOfNav(this.getActive());
4138         if (i  < 1) {
4139             return;
4140         }
4141         this.setActiveItem(this.navItems[i-1]);
4142     },
4143     clearWasActive : function(except) {
4144         Roo.each(this.navItems, function(e) {
4145             if (e.tabId != except.tabId && e.was_active) {
4146                e.was_active = false;
4147                return false;
4148             }
4149             return true;
4150             
4151         });
4152     },
4153     getWasActive : function ()
4154     {
4155         var r = false;
4156         Roo.each(this.navItems, function(e) {
4157             if (e.was_active) {
4158                r = e;
4159                return false;
4160             }
4161             return true;
4162             
4163         });
4164         return r;
4165     }
4166     
4167     
4168 });
4169
4170  
4171 Roo.apply(Roo.bootstrap.NavGroup, {
4172     
4173     groups: {},
4174      /**
4175     * register a Navigation Group
4176     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4177     */
4178     register : function(navgrp)
4179     {
4180         this.groups[navgrp.navId] = navgrp;
4181         
4182     },
4183     /**
4184     * fetch a Navigation Group based on the navigation ID
4185     * @param {string} the navgroup to add
4186     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4187     */
4188     get: function(navId) {
4189         if (typeof(this.groups[navId]) == 'undefined') {
4190             return false;
4191             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4192         }
4193         return this.groups[navId] ;
4194     }
4195     
4196     
4197     
4198 });
4199
4200  /*
4201  * - LGPL
4202  *
4203  * row
4204  * 
4205  */
4206
4207 /**
4208  * @class Roo.bootstrap.NavItem
4209  * @extends Roo.bootstrap.Component
4210  * Bootstrap Navbar.NavItem class
4211  * @cfg {String} href  link to
4212  * @cfg {String} html content of button
4213  * @cfg {String} badge text inside badge
4214  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4215  * @cfg {String} glyphicon name of glyphicon
4216  * @cfg {String} icon name of font awesome icon
4217  * @cfg {Boolean} active Is item active
4218  * @cfg {Boolean} disabled Is item disabled
4219  
4220  * @cfg {Boolean} preventDefault (true | false) default false
4221  * @cfg {String} tabId the tab that this item activates.
4222  * @cfg {String} tagtype (a|span) render as a href or span?
4223  * @cfg {Boolean} animateRef (true|false) link to element default false  
4224   
4225  * @constructor
4226  * Create a new Navbar Item
4227  * @param {Object} config The config object
4228  */
4229 Roo.bootstrap.NavItem = function(config){
4230     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4231     this.addEvents({
4232         // raw events
4233         /**
4234          * @event click
4235          * The raw click event for the entire grid.
4236          * @param {Roo.EventObject} e
4237          */
4238         "click" : true,
4239          /**
4240             * @event changed
4241             * Fires when the active item active state changes
4242             * @param {Roo.bootstrap.NavItem} this
4243             * @param {boolean} state the new state
4244              
4245          */
4246         'changed': true,
4247         /**
4248             * @event scrollto
4249             * Fires when scroll to element
4250             * @param {Roo.bootstrap.NavItem} this
4251             * @param {Object} options
4252             * @param {Roo.EventObject} e
4253              
4254          */
4255         'scrollto': true
4256     });
4257    
4258 };
4259
4260 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4261     
4262     href: false,
4263     html: '',
4264     badge: '',
4265     icon: false,
4266     glyphicon: false,
4267     active: false,
4268     preventDefault : false,
4269     tabId : false,
4270     tagtype : 'a',
4271     disabled : false,
4272     animateRef : false,
4273     was_active : false,
4274     
4275     getAutoCreate : function(){
4276          
4277         var cfg = {
4278             tag: 'li',
4279             cls: 'nav-item'
4280             
4281         };
4282         
4283         if (this.active) {
4284             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4285         }
4286         if (this.disabled) {
4287             cfg.cls += ' disabled';
4288         }
4289         
4290         if (this.href || this.html || this.glyphicon || this.icon) {
4291             cfg.cn = [
4292                 {
4293                     tag: this.tagtype,
4294                     href : this.href || "#",
4295                     html: this.html || ''
4296                 }
4297             ];
4298             
4299             if (this.icon) {
4300                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4301             }
4302
4303             if(this.glyphicon) {
4304                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4305             }
4306             
4307             if (this.menu) {
4308                 
4309                 cfg.cn[0].html += " <span class='caret'></span>";
4310              
4311             }
4312             
4313             if (this.badge !== '') {
4314                  
4315                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4316             }
4317         }
4318         
4319         
4320         
4321         return cfg;
4322     },
4323     initEvents: function() 
4324     {
4325         if (typeof (this.menu) != 'undefined') {
4326             this.menu.parentType = this.xtype;
4327             this.menu.triggerEl = this.el;
4328             this.menu = this.addxtype(Roo.apply({}, this.menu));
4329         }
4330         
4331         this.el.select('a',true).on('click', this.onClick, this);
4332         
4333         if(this.tagtype == 'span'){
4334             this.el.select('span',true).on('click', this.onClick, this);
4335         }
4336        
4337         // at this point parent should be available..
4338         this.parent().register(this);
4339     },
4340     
4341     onClick : function(e)
4342     {
4343         if (e.getTarget('.dropdown-menu-item')) {
4344             // did you click on a menu itemm.... - then don't trigger onclick..
4345             return;
4346         }
4347         
4348         if(
4349                 this.preventDefault || 
4350                 this.href == '#' 
4351         ){
4352             Roo.log("NavItem - prevent Default?");
4353             e.preventDefault();
4354         }
4355         
4356         if (this.disabled) {
4357             return;
4358         }
4359         
4360         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4361         if (tg && tg.transition) {
4362             Roo.log("waiting for the transitionend");
4363             return;
4364         }
4365         
4366         
4367         
4368         //Roo.log("fire event clicked");
4369         if(this.fireEvent('click', this, e) === false){
4370             return;
4371         };
4372         
4373         if(this.tagtype == 'span'){
4374             return;
4375         }
4376         
4377         //Roo.log(this.href);
4378         var ael = this.el.select('a',true).first();
4379         //Roo.log(ael);
4380         
4381         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4382             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4383             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4384                 return; // ignore... - it's a 'hash' to another page.
4385             }
4386             Roo.log("NavItem - prevent Default?");
4387             e.preventDefault();
4388             this.scrollToElement(e);
4389         }
4390         
4391         
4392         var p =  this.parent();
4393    
4394         if (['tabs','pills'].indexOf(p.type)!==-1) {
4395             if (typeof(p.setActiveItem) !== 'undefined') {
4396                 p.setActiveItem(this);
4397             }
4398         }
4399         
4400         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4401         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4402             // remove the collapsed menu expand...
4403             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4404         }
4405     },
4406     
4407     isActive: function () {
4408         return this.active
4409     },
4410     setActive : function(state, fire, is_was_active)
4411     {
4412         if (this.active && !state && this.navId) {
4413             this.was_active = true;
4414             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4415             if (nv) {
4416                 nv.clearWasActive(this);
4417             }
4418             
4419         }
4420         this.active = state;
4421         
4422         if (!state ) {
4423             this.el.removeClass('active');
4424         } else if (!this.el.hasClass('active')) {
4425             this.el.addClass('active');
4426         }
4427         if (fire) {
4428             this.fireEvent('changed', this, state);
4429         }
4430         
4431         // show a panel if it's registered and related..
4432         
4433         if (!this.navId || !this.tabId || !state || is_was_active) {
4434             return;
4435         }
4436         
4437         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4438         if (!tg) {
4439             return;
4440         }
4441         var pan = tg.getPanelByName(this.tabId);
4442         if (!pan) {
4443             return;
4444         }
4445         // if we can not flip to new panel - go back to old nav highlight..
4446         if (false == tg.showPanel(pan)) {
4447             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4448             if (nv) {
4449                 var onav = nv.getWasActive();
4450                 if (onav) {
4451                     onav.setActive(true, false, true);
4452                 }
4453             }
4454             
4455         }
4456         
4457         
4458         
4459     },
4460      // this should not be here...
4461     setDisabled : function(state)
4462     {
4463         this.disabled = state;
4464         if (!state ) {
4465             this.el.removeClass('disabled');
4466         } else if (!this.el.hasClass('disabled')) {
4467             this.el.addClass('disabled');
4468         }
4469         
4470     },
4471     
4472     /**
4473      * Fetch the element to display the tooltip on.
4474      * @return {Roo.Element} defaults to this.el
4475      */
4476     tooltipEl : function()
4477     {
4478         return this.el.select('' + this.tagtype + '', true).first();
4479     },
4480     
4481     scrollToElement : function(e)
4482     {
4483         var c = document.body;
4484         
4485         /*
4486          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4487          */
4488         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4489             c = document.documentElement;
4490         }
4491         
4492         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4493         
4494         if(!target){
4495             return;
4496         }
4497
4498         var o = target.calcOffsetsTo(c);
4499         
4500         var options = {
4501             target : target,
4502             value : o[1]
4503         };
4504         
4505         this.fireEvent('scrollto', this, options, e);
4506         
4507         Roo.get(c).scrollTo('top', options.value, true);
4508         
4509         return;
4510     }
4511 });
4512  
4513
4514  /*
4515  * - LGPL
4516  *
4517  * sidebar item
4518  *
4519  *  li
4520  *    <span> icon </span>
4521  *    <span> text </span>
4522  *    <span>badge </span>
4523  */
4524
4525 /**
4526  * @class Roo.bootstrap.NavSidebarItem
4527  * @extends Roo.bootstrap.NavItem
4528  * Bootstrap Navbar.NavSidebarItem class
4529  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4530  * {bool} open is the menu open
4531  * @constructor
4532  * Create a new Navbar Button
4533  * @param {Object} config The config object
4534  */
4535 Roo.bootstrap.NavSidebarItem = function(config){
4536     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4537     this.addEvents({
4538         // raw events
4539         /**
4540          * @event click
4541          * The raw click event for the entire grid.
4542          * @param {Roo.EventObject} e
4543          */
4544         "click" : true,
4545          /**
4546             * @event changed
4547             * Fires when the active item active state changes
4548             * @param {Roo.bootstrap.NavSidebarItem} this
4549             * @param {boolean} state the new state
4550              
4551          */
4552         'changed': true
4553     });
4554    
4555 };
4556
4557 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4558     
4559     badgeWeight : 'default',
4560     
4561     open: false,
4562     
4563     getAutoCreate : function(){
4564         
4565         
4566         var a = {
4567                 tag: 'a',
4568                 href : this.href || '#',
4569                 cls: '',
4570                 html : '',
4571                 cn : []
4572         };
4573         var cfg = {
4574             tag: 'li',
4575             cls: '',
4576             cn: [ a ]
4577         };
4578         var span = {
4579             tag: 'span',
4580             html : this.html || ''
4581         };
4582         
4583         
4584         if (this.active) {
4585             cfg.cls += ' active';
4586         }
4587         
4588         if (this.disabled) {
4589             cfg.cls += ' disabled';
4590         }
4591         if (this.open) {
4592             cfg.cls += ' open x-open';
4593         }
4594         // left icon..
4595         if (this.glyphicon || this.icon) {
4596             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4597             a.cn.push({ tag : 'i', cls : c }) ;
4598         }
4599         // html..
4600         a.cn.push(span);
4601         // then badge..
4602         if (this.badge !== '') {
4603             
4604             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4605         }
4606         // fi
4607         if (this.menu) {
4608             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4609             a.cls += 'dropdown-toggle treeview' ;
4610         }
4611         
4612         return cfg;
4613          
4614            
4615     },
4616     
4617     initEvents : function()
4618     { 
4619         if (typeof (this.menu) != 'undefined') {
4620             this.menu.parentType = this.xtype;
4621             this.menu.triggerEl = this.el;
4622             this.menu = this.addxtype(Roo.apply({}, this.menu));
4623         }
4624         
4625         this.el.on('click', this.onClick, this);
4626        
4627     
4628         if(this.badge !== ''){
4629  
4630             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4631         }
4632         
4633     },
4634     
4635     onClick : function(e)
4636     {
4637         if(this.disabled){
4638             e.preventDefault();
4639             return;
4640         }
4641         
4642         if(this.preventDefault){
4643             e.preventDefault();
4644         }
4645         
4646         this.fireEvent('click', this);
4647     },
4648     
4649     disable : function()
4650     {
4651         this.setDisabled(true);
4652     },
4653     
4654     enable : function()
4655     {
4656         this.setDisabled(false);
4657     },
4658     
4659     setDisabled : function(state)
4660     {
4661         if(this.disabled == state){
4662             return;
4663         }
4664         
4665         this.disabled = state;
4666         
4667         if (state) {
4668             this.el.addClass('disabled');
4669             return;
4670         }
4671         
4672         this.el.removeClass('disabled');
4673         
4674         return;
4675     },
4676     
4677     setActive : function(state)
4678     {
4679         if(this.active == state){
4680             return;
4681         }
4682         
4683         this.active = state;
4684         
4685         if (state) {
4686             this.el.addClass('active');
4687             return;
4688         }
4689         
4690         this.el.removeClass('active');
4691         
4692         return;
4693     },
4694     
4695     isActive: function () 
4696     {
4697         return this.active;
4698     },
4699     
4700     setBadge : function(str)
4701     {
4702         if(!this.badgeEl){
4703             return;
4704         }
4705         
4706         this.badgeEl.dom.innerHTML = str;
4707     }
4708     
4709    
4710      
4711  
4712 });
4713  
4714
4715  /*
4716  * - LGPL
4717  *
4718  * row
4719  * 
4720  */
4721
4722 /**
4723  * @class Roo.bootstrap.Row
4724  * @extends Roo.bootstrap.Component
4725  * Bootstrap Row class (contains columns...)
4726  * 
4727  * @constructor
4728  * Create a new Row
4729  * @param {Object} config The config object
4730  */
4731
4732 Roo.bootstrap.Row = function(config){
4733     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4734 };
4735
4736 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4737     
4738     getAutoCreate : function(){
4739        return {
4740             cls: 'row clearfix'
4741        };
4742     }
4743     
4744     
4745 });
4746
4747  
4748
4749  /*
4750  * - LGPL
4751  *
4752  * element
4753  * 
4754  */
4755
4756 /**
4757  * @class Roo.bootstrap.Element
4758  * @extends Roo.bootstrap.Component
4759  * Bootstrap Element class
4760  * @cfg {String} html contents of the element
4761  * @cfg {String} tag tag of the element
4762  * @cfg {String} cls class of the element
4763  * @cfg {Boolean} preventDefault (true|false) default false
4764  * @cfg {Boolean} clickable (true|false) default false
4765  * 
4766  * @constructor
4767  * Create a new Element
4768  * @param {Object} config The config object
4769  */
4770
4771 Roo.bootstrap.Element = function(config){
4772     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4773     
4774     this.addEvents({
4775         // raw events
4776         /**
4777          * @event click
4778          * When a element is chick
4779          * @param {Roo.bootstrap.Element} this
4780          * @param {Roo.EventObject} e
4781          */
4782         "click" : true
4783     });
4784 };
4785
4786 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4787     
4788     tag: 'div',
4789     cls: '',
4790     html: '',
4791     preventDefault: false, 
4792     clickable: false,
4793     
4794     getAutoCreate : function(){
4795         
4796         var cfg = {
4797             tag: this.tag,
4798             cls: this.cls,
4799             html: this.html
4800         };
4801         
4802         return cfg;
4803     },
4804     
4805     initEvents: function() 
4806     {
4807         Roo.bootstrap.Element.superclass.initEvents.call(this);
4808         
4809         if(this.clickable){
4810             this.el.on('click', this.onClick, this);
4811         }
4812         
4813     },
4814     
4815     onClick : function(e)
4816     {
4817         if(this.preventDefault){
4818             e.preventDefault();
4819         }
4820         
4821         this.fireEvent('click', this, e);
4822     },
4823     
4824     getValue : function()
4825     {
4826         return this.el.dom.innerHTML;
4827     },
4828     
4829     setValue : function(value)
4830     {
4831         this.el.dom.innerHTML = value;
4832     }
4833    
4834 });
4835
4836  
4837
4838  /*
4839  * - LGPL
4840  *
4841  * pagination
4842  * 
4843  */
4844
4845 /**
4846  * @class Roo.bootstrap.Pagination
4847  * @extends Roo.bootstrap.Component
4848  * Bootstrap Pagination class
4849  * @cfg {String} size xs | sm | md | lg
4850  * @cfg {Boolean} inverse false | true
4851  * 
4852  * @constructor
4853  * Create a new Pagination
4854  * @param {Object} config The config object
4855  */
4856
4857 Roo.bootstrap.Pagination = function(config){
4858     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4859 };
4860
4861 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4862     
4863     cls: false,
4864     size: false,
4865     inverse: false,
4866     
4867     getAutoCreate : function(){
4868         var cfg = {
4869             tag: 'ul',
4870                 cls: 'pagination'
4871         };
4872         if (this.inverse) {
4873             cfg.cls += ' inverse';
4874         }
4875         if (this.html) {
4876             cfg.html=this.html;
4877         }
4878         if (this.cls) {
4879             cfg.cls += " " + this.cls;
4880         }
4881         return cfg;
4882     }
4883    
4884 });
4885
4886  
4887
4888  /*
4889  * - LGPL
4890  *
4891  * Pagination item
4892  * 
4893  */
4894
4895
4896 /**
4897  * @class Roo.bootstrap.PaginationItem
4898  * @extends Roo.bootstrap.Component
4899  * Bootstrap PaginationItem class
4900  * @cfg {String} html text
4901  * @cfg {String} href the link
4902  * @cfg {Boolean} preventDefault (true | false) default true
4903  * @cfg {Boolean} active (true | false) default false
4904  * @cfg {Boolean} disabled default false
4905  * 
4906  * 
4907  * @constructor
4908  * Create a new PaginationItem
4909  * @param {Object} config The config object
4910  */
4911
4912
4913 Roo.bootstrap.PaginationItem = function(config){
4914     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4915     this.addEvents({
4916         // raw events
4917         /**
4918          * @event click
4919          * The raw click event for the entire grid.
4920          * @param {Roo.EventObject} e
4921          */
4922         "click" : true
4923     });
4924 };
4925
4926 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4927     
4928     href : false,
4929     html : false,
4930     preventDefault: true,
4931     active : false,
4932     cls : false,
4933     disabled: false,
4934     
4935     getAutoCreate : function(){
4936         var cfg= {
4937             tag: 'li',
4938             cn: [
4939                 {
4940                     tag : 'a',
4941                     href : this.href ? this.href : '#',
4942                     html : this.html ? this.html : ''
4943                 }
4944             ]
4945         };
4946         
4947         if(this.cls){
4948             cfg.cls = this.cls;
4949         }
4950         
4951         if(this.disabled){
4952             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4953         }
4954         
4955         if(this.active){
4956             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4957         }
4958         
4959         return cfg;
4960     },
4961     
4962     initEvents: function() {
4963         
4964         this.el.on('click', this.onClick, this);
4965         
4966     },
4967     onClick : function(e)
4968     {
4969         Roo.log('PaginationItem on click ');
4970         if(this.preventDefault){
4971             e.preventDefault();
4972         }
4973         
4974         if(this.disabled){
4975             return;
4976         }
4977         
4978         this.fireEvent('click', this, e);
4979     }
4980    
4981 });
4982
4983  
4984
4985  /*
4986  * - LGPL
4987  *
4988  * slider
4989  * 
4990  */
4991
4992
4993 /**
4994  * @class Roo.bootstrap.Slider
4995  * @extends Roo.bootstrap.Component
4996  * Bootstrap Slider class
4997  *    
4998  * @constructor
4999  * Create a new Slider
5000  * @param {Object} config The config object
5001  */
5002
5003 Roo.bootstrap.Slider = function(config){
5004     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5005 };
5006
5007 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5008     
5009     getAutoCreate : function(){
5010         
5011         var cfg = {
5012             tag: 'div',
5013             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5014             cn: [
5015                 {
5016                     tag: 'a',
5017                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5018                 }
5019             ]
5020         };
5021         
5022         return cfg;
5023     }
5024    
5025 });
5026
5027  /*
5028  * Based on:
5029  * Ext JS Library 1.1.1
5030  * Copyright(c) 2006-2007, Ext JS, LLC.
5031  *
5032  * Originally Released Under LGPL - original licence link has changed is not relivant.
5033  *
5034  * Fork - LGPL
5035  * <script type="text/javascript">
5036  */
5037  
5038
5039 /**
5040  * @class Roo.grid.ColumnModel
5041  * @extends Roo.util.Observable
5042  * This is the default implementation of a ColumnModel used by the Grid. It defines
5043  * the columns in the grid.
5044  * <br>Usage:<br>
5045  <pre><code>
5046  var colModel = new Roo.grid.ColumnModel([
5047         {header: "Ticker", width: 60, sortable: true, locked: true},
5048         {header: "Company Name", width: 150, sortable: true},
5049         {header: "Market Cap.", width: 100, sortable: true},
5050         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5051         {header: "Employees", width: 100, sortable: true, resizable: false}
5052  ]);
5053  </code></pre>
5054  * <p>
5055  
5056  * The config options listed for this class are options which may appear in each
5057  * individual column definition.
5058  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5059  * @constructor
5060  * @param {Object} config An Array of column config objects. See this class's
5061  * config objects for details.
5062 */
5063 Roo.grid.ColumnModel = function(config){
5064         /**
5065      * The config passed into the constructor
5066      */
5067     this.config = config;
5068     this.lookup = {};
5069
5070     // if no id, create one
5071     // if the column does not have a dataIndex mapping,
5072     // map it to the order it is in the config
5073     for(var i = 0, len = config.length; i < len; i++){
5074         var c = config[i];
5075         if(typeof c.dataIndex == "undefined"){
5076             c.dataIndex = i;
5077         }
5078         if(typeof c.renderer == "string"){
5079             c.renderer = Roo.util.Format[c.renderer];
5080         }
5081         if(typeof c.id == "undefined"){
5082             c.id = Roo.id();
5083         }
5084         if(c.editor && c.editor.xtype){
5085             c.editor  = Roo.factory(c.editor, Roo.grid);
5086         }
5087         if(c.editor && c.editor.isFormField){
5088             c.editor = new Roo.grid.GridEditor(c.editor);
5089         }
5090         this.lookup[c.id] = c;
5091     }
5092
5093     /**
5094      * The width of columns which have no width specified (defaults to 100)
5095      * @type Number
5096      */
5097     this.defaultWidth = 100;
5098
5099     /**
5100      * Default sortable of columns which have no sortable specified (defaults to false)
5101      * @type Boolean
5102      */
5103     this.defaultSortable = false;
5104
5105     this.addEvents({
5106         /**
5107              * @event widthchange
5108              * Fires when the width of a column changes.
5109              * @param {ColumnModel} this
5110              * @param {Number} columnIndex The column index
5111              * @param {Number} newWidth The new width
5112              */
5113             "widthchange": true,
5114         /**
5115              * @event headerchange
5116              * Fires when the text of a header changes.
5117              * @param {ColumnModel} this
5118              * @param {Number} columnIndex The column index
5119              * @param {Number} newText The new header text
5120              */
5121             "headerchange": true,
5122         /**
5123              * @event hiddenchange
5124              * Fires when a column is hidden or "unhidden".
5125              * @param {ColumnModel} this
5126              * @param {Number} columnIndex The column index
5127              * @param {Boolean} hidden true if hidden, false otherwise
5128              */
5129             "hiddenchange": true,
5130             /**
5131          * @event columnmoved
5132          * Fires when a column is moved.
5133          * @param {ColumnModel} this
5134          * @param {Number} oldIndex
5135          * @param {Number} newIndex
5136          */
5137         "columnmoved" : true,
5138         /**
5139          * @event columlockchange
5140          * Fires when a column's locked state is changed
5141          * @param {ColumnModel} this
5142          * @param {Number} colIndex
5143          * @param {Boolean} locked true if locked
5144          */
5145         "columnlockchange" : true
5146     });
5147     Roo.grid.ColumnModel.superclass.constructor.call(this);
5148 };
5149 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5150     /**
5151      * @cfg {String} header The header text to display in the Grid view.
5152      */
5153     /**
5154      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5155      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5156      * specified, the column's index is used as an index into the Record's data Array.
5157      */
5158     /**
5159      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5160      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5161      */
5162     /**
5163      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5164      * Defaults to the value of the {@link #defaultSortable} property.
5165      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5166      */
5167     /**
5168      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5169      */
5170     /**
5171      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5172      */
5173     /**
5174      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5175      */
5176     /**
5177      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5178      */
5179     /**
5180      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5181      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5182      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5183      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5184      */
5185        /**
5186      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5187      */
5188     /**
5189      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5190      */
5191     /**
5192      * @cfg {String} cursor (Optional)
5193      */
5194     /**
5195      * @cfg {String} tooltip (Optional)
5196      */
5197     /**
5198      * @cfg {Number} xs (Optional)
5199      */
5200     /**
5201      * @cfg {Number} sm (Optional)
5202      */
5203     /**
5204      * @cfg {Number} md (Optional)
5205      */
5206     /**
5207      * @cfg {Number} lg (Optional)
5208      */
5209     /**
5210      * Returns the id of the column at the specified index.
5211      * @param {Number} index The column index
5212      * @return {String} the id
5213      */
5214     getColumnId : function(index){
5215         return this.config[index].id;
5216     },
5217
5218     /**
5219      * Returns the column for a specified id.
5220      * @param {String} id The column id
5221      * @return {Object} the column
5222      */
5223     getColumnById : function(id){
5224         return this.lookup[id];
5225     },
5226
5227     
5228     /**
5229      * Returns the column for a specified dataIndex.
5230      * @param {String} dataIndex The column dataIndex
5231      * @return {Object|Boolean} the column or false if not found
5232      */
5233     getColumnByDataIndex: function(dataIndex){
5234         var index = this.findColumnIndex(dataIndex);
5235         return index > -1 ? this.config[index] : false;
5236     },
5237     
5238     /**
5239      * Returns the index for a specified column id.
5240      * @param {String} id The column id
5241      * @return {Number} the index, or -1 if not found
5242      */
5243     getIndexById : function(id){
5244         for(var i = 0, len = this.config.length; i < len; i++){
5245             if(this.config[i].id == id){
5246                 return i;
5247             }
5248         }
5249         return -1;
5250     },
5251     
5252     /**
5253      * Returns the index for a specified column dataIndex.
5254      * @param {String} dataIndex The column dataIndex
5255      * @return {Number} the index, or -1 if not found
5256      */
5257     
5258     findColumnIndex : function(dataIndex){
5259         for(var i = 0, len = this.config.length; i < len; i++){
5260             if(this.config[i].dataIndex == dataIndex){
5261                 return i;
5262             }
5263         }
5264         return -1;
5265     },
5266     
5267     
5268     moveColumn : function(oldIndex, newIndex){
5269         var c = this.config[oldIndex];
5270         this.config.splice(oldIndex, 1);
5271         this.config.splice(newIndex, 0, c);
5272         this.dataMap = null;
5273         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5274     },
5275
5276     isLocked : function(colIndex){
5277         return this.config[colIndex].locked === true;
5278     },
5279
5280     setLocked : function(colIndex, value, suppressEvent){
5281         if(this.isLocked(colIndex) == value){
5282             return;
5283         }
5284         this.config[colIndex].locked = value;
5285         if(!suppressEvent){
5286             this.fireEvent("columnlockchange", this, colIndex, value);
5287         }
5288     },
5289
5290     getTotalLockedWidth : function(){
5291         var totalWidth = 0;
5292         for(var i = 0; i < this.config.length; i++){
5293             if(this.isLocked(i) && !this.isHidden(i)){
5294                 this.totalWidth += this.getColumnWidth(i);
5295             }
5296         }
5297         return totalWidth;
5298     },
5299
5300     getLockedCount : function(){
5301         for(var i = 0, len = this.config.length; i < len; i++){
5302             if(!this.isLocked(i)){
5303                 return i;
5304             }
5305         }
5306         
5307         return this.config.length;
5308     },
5309
5310     /**
5311      * Returns the number of columns.
5312      * @return {Number}
5313      */
5314     getColumnCount : function(visibleOnly){
5315         if(visibleOnly === true){
5316             var c = 0;
5317             for(var i = 0, len = this.config.length; i < len; i++){
5318                 if(!this.isHidden(i)){
5319                     c++;
5320                 }
5321             }
5322             return c;
5323         }
5324         return this.config.length;
5325     },
5326
5327     /**
5328      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5329      * @param {Function} fn
5330      * @param {Object} scope (optional)
5331      * @return {Array} result
5332      */
5333     getColumnsBy : function(fn, scope){
5334         var r = [];
5335         for(var i = 0, len = this.config.length; i < len; i++){
5336             var c = this.config[i];
5337             if(fn.call(scope||this, c, i) === true){
5338                 r[r.length] = c;
5339             }
5340         }
5341         return r;
5342     },
5343
5344     /**
5345      * Returns true if the specified column is sortable.
5346      * @param {Number} col The column index
5347      * @return {Boolean}
5348      */
5349     isSortable : function(col){
5350         if(typeof this.config[col].sortable == "undefined"){
5351             return this.defaultSortable;
5352         }
5353         return this.config[col].sortable;
5354     },
5355
5356     /**
5357      * Returns the rendering (formatting) function defined for the column.
5358      * @param {Number} col The column index.
5359      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5360      */
5361     getRenderer : function(col){
5362         if(!this.config[col].renderer){
5363             return Roo.grid.ColumnModel.defaultRenderer;
5364         }
5365         return this.config[col].renderer;
5366     },
5367
5368     /**
5369      * Sets the rendering (formatting) function for a column.
5370      * @param {Number} col The column index
5371      * @param {Function} fn The function to use to process the cell's raw data
5372      * to return HTML markup for the grid view. The render function is called with
5373      * the following parameters:<ul>
5374      * <li>Data value.</li>
5375      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5376      * <li>css A CSS style string to apply to the table cell.</li>
5377      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5378      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5379      * <li>Row index</li>
5380      * <li>Column index</li>
5381      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5382      */
5383     setRenderer : function(col, fn){
5384         this.config[col].renderer = fn;
5385     },
5386
5387     /**
5388      * Returns the width for the specified column.
5389      * @param {Number} col The column index
5390      * @return {Number}
5391      */
5392     getColumnWidth : function(col){
5393         return this.config[col].width * 1 || this.defaultWidth;
5394     },
5395
5396     /**
5397      * Sets the width for a column.
5398      * @param {Number} col The column index
5399      * @param {Number} width The new width
5400      */
5401     setColumnWidth : function(col, width, suppressEvent){
5402         this.config[col].width = width;
5403         this.totalWidth = null;
5404         if(!suppressEvent){
5405              this.fireEvent("widthchange", this, col, width);
5406         }
5407     },
5408
5409     /**
5410      * Returns the total width of all columns.
5411      * @param {Boolean} includeHidden True to include hidden column widths
5412      * @return {Number}
5413      */
5414     getTotalWidth : function(includeHidden){
5415         if(!this.totalWidth){
5416             this.totalWidth = 0;
5417             for(var i = 0, len = this.config.length; i < len; i++){
5418                 if(includeHidden || !this.isHidden(i)){
5419                     this.totalWidth += this.getColumnWidth(i);
5420                 }
5421             }
5422         }
5423         return this.totalWidth;
5424     },
5425
5426     /**
5427      * Returns the header for the specified column.
5428      * @param {Number} col The column index
5429      * @return {String}
5430      */
5431     getColumnHeader : function(col){
5432         return this.config[col].header;
5433     },
5434
5435     /**
5436      * Sets the header for a column.
5437      * @param {Number} col The column index
5438      * @param {String} header The new header
5439      */
5440     setColumnHeader : function(col, header){
5441         this.config[col].header = header;
5442         this.fireEvent("headerchange", this, col, header);
5443     },
5444
5445     /**
5446      * Returns the tooltip for the specified column.
5447      * @param {Number} col The column index
5448      * @return {String}
5449      */
5450     getColumnTooltip : function(col){
5451             return this.config[col].tooltip;
5452     },
5453     /**
5454      * Sets the tooltip for a column.
5455      * @param {Number} col The column index
5456      * @param {String} tooltip The new tooltip
5457      */
5458     setColumnTooltip : function(col, tooltip){
5459             this.config[col].tooltip = tooltip;
5460     },
5461
5462     /**
5463      * Returns the dataIndex for the specified column.
5464      * @param {Number} col The column index
5465      * @return {Number}
5466      */
5467     getDataIndex : function(col){
5468         return this.config[col].dataIndex;
5469     },
5470
5471     /**
5472      * Sets the dataIndex for a column.
5473      * @param {Number} col The column index
5474      * @param {Number} dataIndex The new dataIndex
5475      */
5476     setDataIndex : function(col, dataIndex){
5477         this.config[col].dataIndex = dataIndex;
5478     },
5479
5480     
5481     
5482     /**
5483      * Returns true if the cell is editable.
5484      * @param {Number} colIndex The column index
5485      * @param {Number} rowIndex The row index - this is nto actually used..?
5486      * @return {Boolean}
5487      */
5488     isCellEditable : function(colIndex, rowIndex){
5489         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5490     },
5491
5492     /**
5493      * Returns the editor defined for the cell/column.
5494      * return false or null to disable editing.
5495      * @param {Number} colIndex The column index
5496      * @param {Number} rowIndex The row index
5497      * @return {Object}
5498      */
5499     getCellEditor : function(colIndex, rowIndex){
5500         return this.config[colIndex].editor;
5501     },
5502
5503     /**
5504      * Sets if a column is editable.
5505      * @param {Number} col The column index
5506      * @param {Boolean} editable True if the column is editable
5507      */
5508     setEditable : function(col, editable){
5509         this.config[col].editable = editable;
5510     },
5511
5512
5513     /**
5514      * Returns true if the column is hidden.
5515      * @param {Number} colIndex The column index
5516      * @return {Boolean}
5517      */
5518     isHidden : function(colIndex){
5519         return this.config[colIndex].hidden;
5520     },
5521
5522
5523     /**
5524      * Returns true if the column width cannot be changed
5525      */
5526     isFixed : function(colIndex){
5527         return this.config[colIndex].fixed;
5528     },
5529
5530     /**
5531      * Returns true if the column can be resized
5532      * @return {Boolean}
5533      */
5534     isResizable : function(colIndex){
5535         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5536     },
5537     /**
5538      * Sets if a column is hidden.
5539      * @param {Number} colIndex The column index
5540      * @param {Boolean} hidden True if the column is hidden
5541      */
5542     setHidden : function(colIndex, hidden){
5543         this.config[colIndex].hidden = hidden;
5544         this.totalWidth = null;
5545         this.fireEvent("hiddenchange", this, colIndex, hidden);
5546     },
5547
5548     /**
5549      * Sets the editor for a column.
5550      * @param {Number} col The column index
5551      * @param {Object} editor The editor object
5552      */
5553     setEditor : function(col, editor){
5554         this.config[col].editor = editor;
5555     }
5556 });
5557
5558 Roo.grid.ColumnModel.defaultRenderer = function(value)
5559 {
5560     if(typeof value == "object") {
5561         return value;
5562     }
5563         if(typeof value == "string" && value.length < 1){
5564             return "&#160;";
5565         }
5566     
5567         return String.format("{0}", value);
5568 };
5569
5570 // Alias for backwards compatibility
5571 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5572 /*
5573  * Based on:
5574  * Ext JS Library 1.1.1
5575  * Copyright(c) 2006-2007, Ext JS, LLC.
5576  *
5577  * Originally Released Under LGPL - original licence link has changed is not relivant.
5578  *
5579  * Fork - LGPL
5580  * <script type="text/javascript">
5581  */
5582  
5583 /**
5584  * @class Roo.LoadMask
5585  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5586  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5587  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5588  * element's UpdateManager load indicator and will be destroyed after the initial load.
5589  * @constructor
5590  * Create a new LoadMask
5591  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5592  * @param {Object} config The config object
5593  */
5594 Roo.LoadMask = function(el, config){
5595     this.el = Roo.get(el);
5596     Roo.apply(this, config);
5597     if(this.store){
5598         this.store.on('beforeload', this.onBeforeLoad, this);
5599         this.store.on('load', this.onLoad, this);
5600         this.store.on('loadexception', this.onLoadException, this);
5601         this.removeMask = false;
5602     }else{
5603         var um = this.el.getUpdateManager();
5604         um.showLoadIndicator = false; // disable the default indicator
5605         um.on('beforeupdate', this.onBeforeLoad, this);
5606         um.on('update', this.onLoad, this);
5607         um.on('failure', this.onLoad, this);
5608         this.removeMask = true;
5609     }
5610 };
5611
5612 Roo.LoadMask.prototype = {
5613     /**
5614      * @cfg {Boolean} removeMask
5615      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5616      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5617      */
5618     /**
5619      * @cfg {String} msg
5620      * The text to display in a centered loading message box (defaults to 'Loading...')
5621      */
5622     msg : 'Loading...',
5623     /**
5624      * @cfg {String} msgCls
5625      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5626      */
5627     msgCls : 'x-mask-loading',
5628
5629     /**
5630      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5631      * @type Boolean
5632      */
5633     disabled: false,
5634
5635     /**
5636      * Disables the mask to prevent it from being displayed
5637      */
5638     disable : function(){
5639        this.disabled = true;
5640     },
5641
5642     /**
5643      * Enables the mask so that it can be displayed
5644      */
5645     enable : function(){
5646         this.disabled = false;
5647     },
5648     
5649     onLoadException : function()
5650     {
5651         Roo.log(arguments);
5652         
5653         if (typeof(arguments[3]) != 'undefined') {
5654             Roo.MessageBox.alert("Error loading",arguments[3]);
5655         } 
5656         /*
5657         try {
5658             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5659                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5660             }   
5661         } catch(e) {
5662             
5663         }
5664         */
5665     
5666         
5667         
5668         this.el.unmask(this.removeMask);
5669     },
5670     // private
5671     onLoad : function()
5672     {
5673         this.el.unmask(this.removeMask);
5674     },
5675
5676     // private
5677     onBeforeLoad : function(){
5678         if(!this.disabled){
5679             this.el.mask(this.msg, this.msgCls);
5680         }
5681     },
5682
5683     // private
5684     destroy : function(){
5685         if(this.store){
5686             this.store.un('beforeload', this.onBeforeLoad, this);
5687             this.store.un('load', this.onLoad, this);
5688             this.store.un('loadexception', this.onLoadException, this);
5689         }else{
5690             var um = this.el.getUpdateManager();
5691             um.un('beforeupdate', this.onBeforeLoad, this);
5692             um.un('update', this.onLoad, this);
5693             um.un('failure', this.onLoad, this);
5694         }
5695     }
5696 };/*
5697  * - LGPL
5698  *
5699  * table
5700  * 
5701  */
5702
5703 /**
5704  * @class Roo.bootstrap.Table
5705  * @extends Roo.bootstrap.Component
5706  * Bootstrap Table class
5707  * @cfg {String} cls table class
5708  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5709  * @cfg {String} bgcolor Specifies the background color for a table
5710  * @cfg {Number} border Specifies whether the table cells should have borders or not
5711  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5712  * @cfg {Number} cellspacing Specifies the space between cells
5713  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5714  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5715  * @cfg {String} sortable Specifies that the table should be sortable
5716  * @cfg {String} summary Specifies a summary of the content of a table
5717  * @cfg {Number} width Specifies the width of a table
5718  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5719  * 
5720  * @cfg {boolean} striped Should the rows be alternative striped
5721  * @cfg {boolean} bordered Add borders to the table
5722  * @cfg {boolean} hover Add hover highlighting
5723  * @cfg {boolean} condensed Format condensed
5724  * @cfg {boolean} responsive Format condensed
5725  * @cfg {Boolean} loadMask (true|false) default false
5726  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5727  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5728  * @cfg {Boolean} rowSelection (true|false) default false
5729  * @cfg {Boolean} cellSelection (true|false) default false
5730  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5731  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5732  
5733  * 
5734  * @constructor
5735  * Create a new Table
5736  * @param {Object} config The config object
5737  */
5738
5739 Roo.bootstrap.Table = function(config){
5740     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5741     
5742   
5743     
5744     // BC...
5745     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5746     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5747     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5748     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5749     
5750     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5751     if (this.sm) {
5752         this.sm.grid = this;
5753         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5754         this.sm = this.selModel;
5755         this.sm.xmodule = this.xmodule || false;
5756     }
5757     
5758     if (this.cm && typeof(this.cm.config) == 'undefined') {
5759         this.colModel = new Roo.grid.ColumnModel(this.cm);
5760         this.cm = this.colModel;
5761         this.cm.xmodule = this.xmodule || false;
5762     }
5763     if (this.store) {
5764         this.store= Roo.factory(this.store, Roo.data);
5765         this.ds = this.store;
5766         this.ds.xmodule = this.xmodule || false;
5767          
5768     }
5769     if (this.footer && this.store) {
5770         this.footer.dataSource = this.ds;
5771         this.footer = Roo.factory(this.footer);
5772     }
5773     
5774     /** @private */
5775     this.addEvents({
5776         /**
5777          * @event cellclick
5778          * Fires when a cell is clicked
5779          * @param {Roo.bootstrap.Table} this
5780          * @param {Roo.Element} el
5781          * @param {Number} rowIndex
5782          * @param {Number} columnIndex
5783          * @param {Roo.EventObject} e
5784          */
5785         "cellclick" : true,
5786         /**
5787          * @event celldblclick
5788          * Fires when a cell is double clicked
5789          * @param {Roo.bootstrap.Table} this
5790          * @param {Roo.Element} el
5791          * @param {Number} rowIndex
5792          * @param {Number} columnIndex
5793          * @param {Roo.EventObject} e
5794          */
5795         "celldblclick" : true,
5796         /**
5797          * @event rowclick
5798          * Fires when a row is clicked
5799          * @param {Roo.bootstrap.Table} this
5800          * @param {Roo.Element} el
5801          * @param {Number} rowIndex
5802          * @param {Roo.EventObject} e
5803          */
5804         "rowclick" : true,
5805         /**
5806          * @event rowdblclick
5807          * Fires when a row is double clicked
5808          * @param {Roo.bootstrap.Table} this
5809          * @param {Roo.Element} el
5810          * @param {Number} rowIndex
5811          * @param {Roo.EventObject} e
5812          */
5813         "rowdblclick" : true,
5814         /**
5815          * @event mouseover
5816          * Fires when a mouseover occur
5817          * @param {Roo.bootstrap.Table} this
5818          * @param {Roo.Element} el
5819          * @param {Number} rowIndex
5820          * @param {Number} columnIndex
5821          * @param {Roo.EventObject} e
5822          */
5823         "mouseover" : true,
5824         /**
5825          * @event mouseout
5826          * Fires when a mouseout occur
5827          * @param {Roo.bootstrap.Table} this
5828          * @param {Roo.Element} el
5829          * @param {Number} rowIndex
5830          * @param {Number} columnIndex
5831          * @param {Roo.EventObject} e
5832          */
5833         "mouseout" : true,
5834         /**
5835          * @event rowclass
5836          * Fires when a row is rendered, so you can change add a style to it.
5837          * @param {Roo.bootstrap.Table} this
5838          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5839          */
5840         'rowclass' : true,
5841           /**
5842          * @event rowsrendered
5843          * Fires when all the  rows have been rendered
5844          * @param {Roo.bootstrap.Table} this
5845          */
5846         'rowsrendered' : true,
5847         /**
5848          * @event contextmenu
5849          * The raw contextmenu event for the entire grid.
5850          * @param {Roo.EventObject} e
5851          */
5852         "contextmenu" : true,
5853         /**
5854          * @event rowcontextmenu
5855          * Fires when a row is right clicked
5856          * @param {Roo.bootstrap.Table} this
5857          * @param {Number} rowIndex
5858          * @param {Roo.EventObject} e
5859          */
5860         "rowcontextmenu" : true,
5861         /**
5862          * @event cellcontextmenu
5863          * Fires when a cell is right clicked
5864          * @param {Roo.bootstrap.Table} this
5865          * @param {Number} rowIndex
5866          * @param {Number} cellIndex
5867          * @param {Roo.EventObject} e
5868          */
5869          "cellcontextmenu" : true,
5870          /**
5871          * @event headercontextmenu
5872          * Fires when a header is right clicked
5873          * @param {Roo.bootstrap.Table} this
5874          * @param {Number} columnIndex
5875          * @param {Roo.EventObject} e
5876          */
5877         "headercontextmenu" : true
5878     });
5879 };
5880
5881 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5882     
5883     cls: false,
5884     align: false,
5885     bgcolor: false,
5886     border: false,
5887     cellpadding: false,
5888     cellspacing: false,
5889     frame: false,
5890     rules: false,
5891     sortable: false,
5892     summary: false,
5893     width: false,
5894     striped : false,
5895     scrollBody : false,
5896     bordered: false,
5897     hover:  false,
5898     condensed : false,
5899     responsive : false,
5900     sm : false,
5901     cm : false,
5902     store : false,
5903     loadMask : false,
5904     footerShow : true,
5905     headerShow : true,
5906   
5907     rowSelection : false,
5908     cellSelection : false,
5909     layout : false,
5910     
5911     // Roo.Element - the tbody
5912     mainBody: false,
5913     // Roo.Element - thead element
5914     mainHead: false,
5915     
5916     container: false, // used by gridpanel...
5917     
5918     getAutoCreate : function()
5919     {
5920         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5921         
5922         cfg = {
5923             tag: 'table',
5924             cls : 'table',
5925             cn : []
5926         };
5927         if (this.scrollBody) {
5928             cfg.cls += ' table-body-fixed';
5929         }    
5930         if (this.striped) {
5931             cfg.cls += ' table-striped';
5932         }
5933         
5934         if (this.hover) {
5935             cfg.cls += ' table-hover';
5936         }
5937         if (this.bordered) {
5938             cfg.cls += ' table-bordered';
5939         }
5940         if (this.condensed) {
5941             cfg.cls += ' table-condensed';
5942         }
5943         if (this.responsive) {
5944             cfg.cls += ' table-responsive';
5945         }
5946         
5947         if (this.cls) {
5948             cfg.cls+=  ' ' +this.cls;
5949         }
5950         
5951         // this lot should be simplifed...
5952         
5953         if (this.align) {
5954             cfg.align=this.align;
5955         }
5956         if (this.bgcolor) {
5957             cfg.bgcolor=this.bgcolor;
5958         }
5959         if (this.border) {
5960             cfg.border=this.border;
5961         }
5962         if (this.cellpadding) {
5963             cfg.cellpadding=this.cellpadding;
5964         }
5965         if (this.cellspacing) {
5966             cfg.cellspacing=this.cellspacing;
5967         }
5968         if (this.frame) {
5969             cfg.frame=this.frame;
5970         }
5971         if (this.rules) {
5972             cfg.rules=this.rules;
5973         }
5974         if (this.sortable) {
5975             cfg.sortable=this.sortable;
5976         }
5977         if (this.summary) {
5978             cfg.summary=this.summary;
5979         }
5980         if (this.width) {
5981             cfg.width=this.width;
5982         }
5983         if (this.layout) {
5984             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5985         }
5986         
5987         if(this.store || this.cm){
5988             if(this.headerShow){
5989                 cfg.cn.push(this.renderHeader());
5990             }
5991             
5992             cfg.cn.push(this.renderBody());
5993             
5994             if(this.footerShow){
5995                 cfg.cn.push(this.renderFooter());
5996             }
5997             // where does this come from?
5998             //cfg.cls+=  ' TableGrid';
5999         }
6000         
6001         return { cn : [ cfg ] };
6002     },
6003     
6004     initEvents : function()
6005     {   
6006         if(!this.store || !this.cm){
6007             return;
6008         }
6009         if (this.selModel) {
6010             this.selModel.initEvents();
6011         }
6012         
6013         
6014         //Roo.log('initEvents with ds!!!!');
6015         
6016         this.mainBody = this.el.select('tbody', true).first();
6017         this.mainHead = this.el.select('thead', true).first();
6018         
6019         
6020         
6021         
6022         var _this = this;
6023         
6024         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6025             e.on('click', _this.sort, _this);
6026         });
6027         
6028         this.mainBody.on("click", this.onClick, this);
6029         this.mainBody.on("dblclick", this.onDblClick, this);
6030         
6031         // why is this done????? = it breaks dialogs??
6032         //this.parent().el.setStyle('position', 'relative');
6033         
6034         
6035         if (this.footer) {
6036             this.footer.parentId = this.id;
6037             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
6038         } 
6039         
6040         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6041         
6042         this.store.on('load', this.onLoad, this);
6043         this.store.on('beforeload', this.onBeforeLoad, this);
6044         this.store.on('update', this.onUpdate, this);
6045         this.store.on('add', this.onAdd, this);
6046         this.store.on("clear", this.clear, this);
6047         
6048         this.el.on("contextmenu", this.onContextMenu, this);
6049         
6050         this.mainBody.on('scroll', this.onBodyScroll, this);
6051         
6052         
6053     },
6054     
6055     onContextMenu : function(e, t)
6056     {
6057         this.processEvent("contextmenu", e);
6058     },
6059     
6060     processEvent : function(name, e)
6061     {
6062         if (name != 'touchstart' ) {
6063             this.fireEvent(name, e);    
6064         }
6065         
6066         var t = e.getTarget();
6067         
6068         var cell = Roo.get(t);
6069         
6070         if(!cell){
6071             return;
6072         }
6073         
6074         if(cell.findParent('tfoot', false, true)){
6075             return;
6076         }
6077         
6078         if(cell.findParent('thead', false, true)){
6079             
6080             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6081                 cell = Roo.get(t).findParent('th', false, true);
6082                 if (!cell) {
6083                     Roo.log("failed to find th in thead?");
6084                     Roo.log(e.getTarget());
6085                     return;
6086                 }
6087             }
6088             
6089             var cellIndex = cell.dom.cellIndex;
6090             
6091             var ename = name == 'touchstart' ? 'click' : name;
6092             this.fireEvent("header" + ename, this, cellIndex, e);
6093             
6094             return;
6095         }
6096         
6097         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6098             cell = Roo.get(t).findParent('td', false, true);
6099             if (!cell) {
6100                 Roo.log("failed to find th in tbody?");
6101                 Roo.log(e.getTarget());
6102                 return;
6103             }
6104         }
6105         
6106         var row = cell.findParent('tr', false, true);
6107         var cellIndex = cell.dom.cellIndex;
6108         var rowIndex = row.dom.rowIndex - 1;
6109         
6110         if(row !== false){
6111             
6112             this.fireEvent("row" + name, this, rowIndex, e);
6113             
6114             if(cell !== false){
6115             
6116                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6117             }
6118         }
6119         
6120     },
6121     
6122     onMouseover : function(e, el)
6123     {
6124         var cell = Roo.get(el);
6125         
6126         if(!cell){
6127             return;
6128         }
6129         
6130         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6131             cell = cell.findParent('td', false, true);
6132         }
6133         
6134         var row = cell.findParent('tr', false, true);
6135         var cellIndex = cell.dom.cellIndex;
6136         var rowIndex = row.dom.rowIndex - 1; // start from 0
6137         
6138         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6139         
6140     },
6141     
6142     onMouseout : function(e, el)
6143     {
6144         var cell = Roo.get(el);
6145         
6146         if(!cell){
6147             return;
6148         }
6149         
6150         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6151             cell = cell.findParent('td', false, true);
6152         }
6153         
6154         var row = cell.findParent('tr', false, true);
6155         var cellIndex = cell.dom.cellIndex;
6156         var rowIndex = row.dom.rowIndex - 1; // start from 0
6157         
6158         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6159         
6160     },
6161     
6162     onClick : function(e, el)
6163     {
6164         var cell = Roo.get(el);
6165         
6166         if(!cell || (!this.cellSelection && !this.rowSelection)){
6167             return;
6168         }
6169         
6170         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6171             cell = cell.findParent('td', false, true);
6172         }
6173         
6174         if(!cell || typeof(cell) == 'undefined'){
6175             return;
6176         }
6177         
6178         var row = cell.findParent('tr', false, true);
6179         
6180         if(!row || typeof(row) == 'undefined'){
6181             return;
6182         }
6183         
6184         var cellIndex = cell.dom.cellIndex;
6185         var rowIndex = this.getRowIndex(row);
6186         
6187         // why??? - should these not be based on SelectionModel?
6188         if(this.cellSelection){
6189             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6190         }
6191         
6192         if(this.rowSelection){
6193             this.fireEvent('rowclick', this, row, rowIndex, e);
6194         }
6195         
6196         
6197     },
6198         
6199     onDblClick : function(e,el)
6200     {
6201         var cell = Roo.get(el);
6202         
6203         if(!cell || (!this.cellSelection && !this.rowSelection)){
6204             return;
6205         }
6206         
6207         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6208             cell = cell.findParent('td', false, true);
6209         }
6210         
6211         if(!cell || typeof(cell) == 'undefined'){
6212             return;
6213         }
6214         
6215         var row = cell.findParent('tr', false, true);
6216         
6217         if(!row || typeof(row) == 'undefined'){
6218             return;
6219         }
6220         
6221         var cellIndex = cell.dom.cellIndex;
6222         var rowIndex = this.getRowIndex(row);
6223         
6224         if(this.cellSelection){
6225             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6226         }
6227         
6228         if(this.rowSelection){
6229             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6230         }
6231     },
6232     
6233     sort : function(e,el)
6234     {
6235         var col = Roo.get(el);
6236         
6237         if(!col.hasClass('sortable')){
6238             return;
6239         }
6240         
6241         var sort = col.attr('sort');
6242         var dir = 'ASC';
6243         
6244         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6245             dir = 'DESC';
6246         }
6247         
6248         this.store.sortInfo = {field : sort, direction : dir};
6249         
6250         if (this.footer) {
6251             Roo.log("calling footer first");
6252             this.footer.onClick('first');
6253         } else {
6254         
6255             this.store.load({ params : { start : 0 } });
6256         }
6257     },
6258     
6259     renderHeader : function()
6260     {
6261         var header = {
6262             tag: 'thead',
6263             cn : []
6264         };
6265         
6266         var cm = this.cm;
6267         this.totalWidth = 0;
6268         
6269         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6270             
6271             var config = cm.config[i];
6272             
6273             var c = {
6274                 tag: 'th',
6275                 style : '',
6276                 html: cm.getColumnHeader(i)
6277             };
6278             
6279             var hh = '';
6280             
6281             if(typeof(config.sortable) != 'undefined' && config.sortable){
6282                 c.cls = 'sortable';
6283                 c.html = '<i class="glyphicon"></i>' + c.html;
6284             }
6285             
6286             if(typeof(config.lgHeader) != 'undefined'){
6287                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6288             }
6289             
6290             if(typeof(config.mdHeader) != 'undefined'){
6291                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6292             }
6293             
6294             if(typeof(config.smHeader) != 'undefined'){
6295                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6296             }
6297             
6298             if(typeof(config.xsHeader) != 'undefined'){
6299                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6300             }
6301             
6302             if(hh.length){
6303                 c.html = hh;
6304             }
6305             
6306             if(typeof(config.tooltip) != 'undefined'){
6307                 c.tooltip = config.tooltip;
6308             }
6309             
6310             if(typeof(config.colspan) != 'undefined'){
6311                 c.colspan = config.colspan;
6312             }
6313             
6314             if(typeof(config.hidden) != 'undefined' && config.hidden){
6315                 c.style += ' display:none;';
6316             }
6317             
6318             if(typeof(config.dataIndex) != 'undefined'){
6319                 c.sort = config.dataIndex;
6320             }
6321             
6322            
6323             
6324             if(typeof(config.align) != 'undefined' && config.align.length){
6325                 c.style += ' text-align:' + config.align + ';';
6326             }
6327             
6328             if(typeof(config.width) != 'undefined'){
6329                 c.style += ' width:' + config.width + 'px;';
6330                 this.totalWidth += config.width;
6331             } else {
6332                 this.totalWidth += 100; // assume minimum of 100 per column?
6333             }
6334             
6335             if(typeof(config.cls) != 'undefined'){
6336                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6337             }
6338             
6339             ['xs','sm','md','lg'].map(function(size){
6340                 
6341                 if(typeof(config[size]) == 'undefined'){
6342                     return;
6343                 }
6344                 
6345                 if (!config[size]) { // 0 = hidden
6346                     c.cls += ' hidden-' + size;
6347                     return;
6348                 }
6349                 
6350                 c.cls += ' col-' + size + '-' + config[size];
6351
6352             });
6353             
6354             header.cn.push(c)
6355         }
6356         
6357         return header;
6358     },
6359     
6360     renderBody : function()
6361     {
6362         var body = {
6363             tag: 'tbody',
6364             cn : [
6365                 {
6366                     tag: 'tr',
6367                     cn : [
6368                         {
6369                             tag : 'td',
6370                             colspan :  this.cm.getColumnCount()
6371                         }
6372                     ]
6373                 }
6374             ]
6375         };
6376         
6377         return body;
6378     },
6379     
6380     renderFooter : function()
6381     {
6382         var footer = {
6383             tag: 'tfoot',
6384             cn : [
6385                 {
6386                     tag: 'tr',
6387                     cn : [
6388                         {
6389                             tag : 'td',
6390                             colspan :  this.cm.getColumnCount()
6391                         }
6392                     ]
6393                 }
6394             ]
6395         };
6396         
6397         return footer;
6398     },
6399     
6400     
6401     
6402     onLoad : function()
6403     {
6404 //        Roo.log('ds onload');
6405         this.clear();
6406         
6407         var _this = this;
6408         var cm = this.cm;
6409         var ds = this.store;
6410         
6411         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6412             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6413             if (_this.store.sortInfo) {
6414                     
6415                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6416                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6417                 }
6418                 
6419                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6420                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6421                 }
6422             }
6423         });
6424         
6425         var tbody =  this.mainBody;
6426               
6427         if(ds.getCount() > 0){
6428             ds.data.each(function(d,rowIndex){
6429                 var row =  this.renderRow(cm, ds, rowIndex);
6430                 
6431                 tbody.createChild(row);
6432                 
6433                 var _this = this;
6434                 
6435                 if(row.cellObjects.length){
6436                     Roo.each(row.cellObjects, function(r){
6437                         _this.renderCellObject(r);
6438                     })
6439                 }
6440                 
6441             }, this);
6442         }
6443         
6444         Roo.each(this.el.select('tbody td', true).elements, function(e){
6445             e.on('mouseover', _this.onMouseover, _this);
6446         });
6447         
6448         Roo.each(this.el.select('tbody td', true).elements, function(e){
6449             e.on('mouseout', _this.onMouseout, _this);
6450         });
6451         this.fireEvent('rowsrendered', this);
6452         //if(this.loadMask){
6453         //    this.maskEl.hide();
6454         //}
6455         
6456         this.autoSize();
6457     },
6458     
6459     
6460     onUpdate : function(ds,record)
6461     {
6462         this.refreshRow(record);
6463         this.autoSize();
6464     },
6465     
6466     onRemove : function(ds, record, index, isUpdate){
6467         if(isUpdate !== true){
6468             this.fireEvent("beforerowremoved", this, index, record);
6469         }
6470         var bt = this.mainBody.dom;
6471         
6472         var rows = this.el.select('tbody > tr', true).elements;
6473         
6474         if(typeof(rows[index]) != 'undefined'){
6475             bt.removeChild(rows[index].dom);
6476         }
6477         
6478 //        if(bt.rows[index]){
6479 //            bt.removeChild(bt.rows[index]);
6480 //        }
6481         
6482         if(isUpdate !== true){
6483             //this.stripeRows(index);
6484             //this.syncRowHeights(index, index);
6485             //this.layout();
6486             this.fireEvent("rowremoved", this, index, record);
6487         }
6488     },
6489     
6490     onAdd : function(ds, records, rowIndex)
6491     {
6492         //Roo.log('on Add called');
6493         // - note this does not handle multiple adding very well..
6494         var bt = this.mainBody.dom;
6495         for (var i =0 ; i < records.length;i++) {
6496             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6497             //Roo.log(records[i]);
6498             //Roo.log(this.store.getAt(rowIndex+i));
6499             this.insertRow(this.store, rowIndex + i, false);
6500             return;
6501         }
6502         
6503     },
6504     
6505     
6506     refreshRow : function(record){
6507         var ds = this.store, index;
6508         if(typeof record == 'number'){
6509             index = record;
6510             record = ds.getAt(index);
6511         }else{
6512             index = ds.indexOf(record);
6513         }
6514         this.insertRow(ds, index, true);
6515         this.autoSize();
6516         this.onRemove(ds, record, index+1, true);
6517         this.autoSize();
6518         //this.syncRowHeights(index, index);
6519         //this.layout();
6520         this.fireEvent("rowupdated", this, index, record);
6521     },
6522     
6523     insertRow : function(dm, rowIndex, isUpdate){
6524         
6525         if(!isUpdate){
6526             this.fireEvent("beforerowsinserted", this, rowIndex);
6527         }
6528             //var s = this.getScrollState();
6529         var row = this.renderRow(this.cm, this.store, rowIndex);
6530         // insert before rowIndex..
6531         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6532         
6533         var _this = this;
6534                 
6535         if(row.cellObjects.length){
6536             Roo.each(row.cellObjects, function(r){
6537                 _this.renderCellObject(r);
6538             })
6539         }
6540             
6541         if(!isUpdate){
6542             this.fireEvent("rowsinserted", this, rowIndex);
6543             //this.syncRowHeights(firstRow, lastRow);
6544             //this.stripeRows(firstRow);
6545             //this.layout();
6546         }
6547         
6548     },
6549     
6550     
6551     getRowDom : function(rowIndex)
6552     {
6553         var rows = this.el.select('tbody > tr', true).elements;
6554         
6555         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6556         
6557     },
6558     // returns the object tree for a tr..
6559   
6560     
6561     renderRow : function(cm, ds, rowIndex) 
6562     {
6563         
6564         var d = ds.getAt(rowIndex);
6565         
6566         var row = {
6567             tag : 'tr',
6568             cn : []
6569         };
6570             
6571         var cellObjects = [];
6572         
6573         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6574             var config = cm.config[i];
6575             
6576             var renderer = cm.getRenderer(i);
6577             var value = '';
6578             var id = false;
6579             
6580             if(typeof(renderer) !== 'undefined'){
6581                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6582             }
6583             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6584             // and are rendered into the cells after the row is rendered - using the id for the element.
6585             
6586             if(typeof(value) === 'object'){
6587                 id = Roo.id();
6588                 cellObjects.push({
6589                     container : id,
6590                     cfg : value 
6591                 })
6592             }
6593             
6594             var rowcfg = {
6595                 record: d,
6596                 rowIndex : rowIndex,
6597                 colIndex : i,
6598                 rowClass : ''
6599             };
6600
6601             this.fireEvent('rowclass', this, rowcfg);
6602             
6603             var td = {
6604                 tag: 'td',
6605                 cls : rowcfg.rowClass,
6606                 style: '',
6607                 html: (typeof(value) === 'object') ? '' : value
6608             };
6609             
6610             if (id) {
6611                 td.id = id;
6612             }
6613             
6614             if(typeof(config.colspan) != 'undefined'){
6615                 td.colspan = config.colspan;
6616             }
6617             
6618             if(typeof(config.hidden) != 'undefined' && config.hidden){
6619                 td.style += ' display:none;';
6620             }
6621             
6622             if(typeof(config.align) != 'undefined' && config.align.length){
6623                 td.style += ' text-align:' + config.align + ';';
6624             }
6625             
6626             if(typeof(config.width) != 'undefined'){
6627                 td.style += ' width:' +  config.width + 'px;';
6628             }
6629             
6630             if(typeof(config.cursor) != 'undefined'){
6631                 td.style += ' cursor:' +  config.cursor + ';';
6632             }
6633             
6634             if(typeof(config.cls) != 'undefined'){
6635                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6636             }
6637             
6638             ['xs','sm','md','lg'].map(function(size){
6639                 
6640                 if(typeof(config[size]) == 'undefined'){
6641                     return;
6642                 }
6643                 
6644                 if (!config[size]) { // 0 = hidden
6645                     td.cls += ' hidden-' + size;
6646                     return;
6647                 }
6648                 
6649                 td.cls += ' col-' + size + '-' + config[size];
6650
6651             });
6652              
6653             row.cn.push(td);
6654            
6655         }
6656         
6657         row.cellObjects = cellObjects;
6658         
6659         return row;
6660           
6661     },
6662     
6663     
6664     
6665     onBeforeLoad : function()
6666     {
6667         //Roo.log('ds onBeforeLoad');
6668         
6669         //this.clear();
6670         
6671         //if(this.loadMask){
6672         //    this.maskEl.show();
6673         //}
6674     },
6675      /**
6676      * Remove all rows
6677      */
6678     clear : function()
6679     {
6680         this.el.select('tbody', true).first().dom.innerHTML = '';
6681     },
6682     /**
6683      * Show or hide a row.
6684      * @param {Number} rowIndex to show or hide
6685      * @param {Boolean} state hide
6686      */
6687     setRowVisibility : function(rowIndex, state)
6688     {
6689         var bt = this.mainBody.dom;
6690         
6691         var rows = this.el.select('tbody > tr', true).elements;
6692         
6693         if(typeof(rows[rowIndex]) == 'undefined'){
6694             return;
6695         }
6696         rows[rowIndex].dom.style.display = state ? '' : 'none';
6697     },
6698     
6699     
6700     getSelectionModel : function(){
6701         if(!this.selModel){
6702             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6703         }
6704         return this.selModel;
6705     },
6706     /*
6707      * Render the Roo.bootstrap object from renderder
6708      */
6709     renderCellObject : function(r)
6710     {
6711         var _this = this;
6712         
6713         var t = r.cfg.render(r.container);
6714         
6715         if(r.cfg.cn){
6716             Roo.each(r.cfg.cn, function(c){
6717                 var child = {
6718                     container: t.getChildContainer(),
6719                     cfg: c
6720                 };
6721                 _this.renderCellObject(child);
6722             })
6723         }
6724     },
6725     
6726     getRowIndex : function(row)
6727     {
6728         var rowIndex = -1;
6729         
6730         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6731             if(el != row){
6732                 return;
6733             }
6734             
6735             rowIndex = index;
6736         });
6737         
6738         return rowIndex;
6739     },
6740      /**
6741      * Returns the grid's underlying element = used by panel.Grid
6742      * @return {Element} The element
6743      */
6744     getGridEl : function(){
6745         return this.el;
6746     },
6747      /**
6748      * Forces a resize - used by panel.Grid
6749      * @return {Element} The element
6750      */
6751     autoSize : function()
6752     {
6753         //var ctr = Roo.get(this.container.dom.parentElement);
6754         var ctr = Roo.get(this.el.dom);
6755         
6756         var thd = this.getGridEl().select('thead',true).first();
6757         var tbd = this.getGridEl().select('tbody', true).first();
6758         var tfd = this.getGridEl().select('tfoot', true).first();
6759         
6760         var cw = ctr.getWidth();
6761         
6762         if (tbd) {
6763             
6764             tbd.setSize(ctr.getWidth(),
6765                         ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6766             );
6767             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6768             cw -= barsize;
6769         }
6770         cw = Math.max(cw, this.totalWidth);
6771         this.getGridEl().select('tr',true).setWidth(cw);
6772         // resize 'expandable coloumn?
6773         
6774         return; // we doe not have a view in this design..
6775         
6776     },
6777     onBodyScroll: function()
6778     {
6779         
6780         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6781         this.mainHead.setStyle({
6782                     'position' : 'relative',
6783                     'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6784         });
6785         
6786         
6787     }
6788 });
6789
6790  
6791
6792  /*
6793  * - LGPL
6794  *
6795  * table cell
6796  * 
6797  */
6798
6799 /**
6800  * @class Roo.bootstrap.TableCell
6801  * @extends Roo.bootstrap.Component
6802  * Bootstrap TableCell class
6803  * @cfg {String} html cell contain text
6804  * @cfg {String} cls cell class
6805  * @cfg {String} tag cell tag (td|th) default td
6806  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6807  * @cfg {String} align Aligns the content in a cell
6808  * @cfg {String} axis Categorizes cells
6809  * @cfg {String} bgcolor Specifies the background color of a cell
6810  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6811  * @cfg {Number} colspan Specifies the number of columns a cell should span
6812  * @cfg {String} headers Specifies one or more header cells a cell is related to
6813  * @cfg {Number} height Sets the height of a cell
6814  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6815  * @cfg {Number} rowspan Sets the number of rows a cell should span
6816  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6817  * @cfg {String} valign Vertical aligns the content in a cell
6818  * @cfg {Number} width Specifies the width of a cell
6819  * 
6820  * @constructor
6821  * Create a new TableCell
6822  * @param {Object} config The config object
6823  */
6824
6825 Roo.bootstrap.TableCell = function(config){
6826     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6827 };
6828
6829 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6830     
6831     html: false,
6832     cls: false,
6833     tag: false,
6834     abbr: false,
6835     align: false,
6836     axis: false,
6837     bgcolor: false,
6838     charoff: false,
6839     colspan: false,
6840     headers: false,
6841     height: false,
6842     nowrap: false,
6843     rowspan: false,
6844     scope: false,
6845     valign: false,
6846     width: false,
6847     
6848     
6849     getAutoCreate : function(){
6850         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6851         
6852         cfg = {
6853             tag: 'td'
6854         };
6855         
6856         if(this.tag){
6857             cfg.tag = this.tag;
6858         }
6859         
6860         if (this.html) {
6861             cfg.html=this.html
6862         }
6863         if (this.cls) {
6864             cfg.cls=this.cls
6865         }
6866         if (this.abbr) {
6867             cfg.abbr=this.abbr
6868         }
6869         if (this.align) {
6870             cfg.align=this.align
6871         }
6872         if (this.axis) {
6873             cfg.axis=this.axis
6874         }
6875         if (this.bgcolor) {
6876             cfg.bgcolor=this.bgcolor
6877         }
6878         if (this.charoff) {
6879             cfg.charoff=this.charoff
6880         }
6881         if (this.colspan) {
6882             cfg.colspan=this.colspan
6883         }
6884         if (this.headers) {
6885             cfg.headers=this.headers
6886         }
6887         if (this.height) {
6888             cfg.height=this.height
6889         }
6890         if (this.nowrap) {
6891             cfg.nowrap=this.nowrap
6892         }
6893         if (this.rowspan) {
6894             cfg.rowspan=this.rowspan
6895         }
6896         if (this.scope) {
6897             cfg.scope=this.scope
6898         }
6899         if (this.valign) {
6900             cfg.valign=this.valign
6901         }
6902         if (this.width) {
6903             cfg.width=this.width
6904         }
6905         
6906         
6907         return cfg;
6908     }
6909    
6910 });
6911
6912  
6913
6914  /*
6915  * - LGPL
6916  *
6917  * table row
6918  * 
6919  */
6920
6921 /**
6922  * @class Roo.bootstrap.TableRow
6923  * @extends Roo.bootstrap.Component
6924  * Bootstrap TableRow class
6925  * @cfg {String} cls row class
6926  * @cfg {String} align Aligns the content in a table row
6927  * @cfg {String} bgcolor Specifies a background color for a table row
6928  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6929  * @cfg {String} valign Vertical aligns the content in a table row
6930  * 
6931  * @constructor
6932  * Create a new TableRow
6933  * @param {Object} config The config object
6934  */
6935
6936 Roo.bootstrap.TableRow = function(config){
6937     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6938 };
6939
6940 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6941     
6942     cls: false,
6943     align: false,
6944     bgcolor: false,
6945     charoff: false,
6946     valign: false,
6947     
6948     getAutoCreate : function(){
6949         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6950         
6951         cfg = {
6952             tag: 'tr'
6953         };
6954             
6955         if(this.cls){
6956             cfg.cls = this.cls;
6957         }
6958         if(this.align){
6959             cfg.align = this.align;
6960         }
6961         if(this.bgcolor){
6962             cfg.bgcolor = this.bgcolor;
6963         }
6964         if(this.charoff){
6965             cfg.charoff = this.charoff;
6966         }
6967         if(this.valign){
6968             cfg.valign = this.valign;
6969         }
6970         
6971         return cfg;
6972     }
6973    
6974 });
6975
6976  
6977
6978  /*
6979  * - LGPL
6980  *
6981  * table body
6982  * 
6983  */
6984
6985 /**
6986  * @class Roo.bootstrap.TableBody
6987  * @extends Roo.bootstrap.Component
6988  * Bootstrap TableBody class
6989  * @cfg {String} cls element class
6990  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6991  * @cfg {String} align Aligns the content inside the element
6992  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6993  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6994  * 
6995  * @constructor
6996  * Create a new TableBody
6997  * @param {Object} config The config object
6998  */
6999
7000 Roo.bootstrap.TableBody = function(config){
7001     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7002 };
7003
7004 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7005     
7006     cls: false,
7007     tag: false,
7008     align: false,
7009     charoff: false,
7010     valign: false,
7011     
7012     getAutoCreate : function(){
7013         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7014         
7015         cfg = {
7016             tag: 'tbody'
7017         };
7018             
7019         if (this.cls) {
7020             cfg.cls=this.cls
7021         }
7022         if(this.tag){
7023             cfg.tag = this.tag;
7024         }
7025         
7026         if(this.align){
7027             cfg.align = this.align;
7028         }
7029         if(this.charoff){
7030             cfg.charoff = this.charoff;
7031         }
7032         if(this.valign){
7033             cfg.valign = this.valign;
7034         }
7035         
7036         return cfg;
7037     }
7038     
7039     
7040 //    initEvents : function()
7041 //    {
7042 //        
7043 //        if(!this.store){
7044 //            return;
7045 //        }
7046 //        
7047 //        this.store = Roo.factory(this.store, Roo.data);
7048 //        this.store.on('load', this.onLoad, this);
7049 //        
7050 //        this.store.load();
7051 //        
7052 //    },
7053 //    
7054 //    onLoad: function () 
7055 //    {   
7056 //        this.fireEvent('load', this);
7057 //    }
7058 //    
7059 //   
7060 });
7061
7062  
7063
7064  /*
7065  * Based on:
7066  * Ext JS Library 1.1.1
7067  * Copyright(c) 2006-2007, Ext JS, LLC.
7068  *
7069  * Originally Released Under LGPL - original licence link has changed is not relivant.
7070  *
7071  * Fork - LGPL
7072  * <script type="text/javascript">
7073  */
7074
7075 // as we use this in bootstrap.
7076 Roo.namespace('Roo.form');
7077  /**
7078  * @class Roo.form.Action
7079  * Internal Class used to handle form actions
7080  * @constructor
7081  * @param {Roo.form.BasicForm} el The form element or its id
7082  * @param {Object} config Configuration options
7083  */
7084
7085  
7086  
7087 // define the action interface
7088 Roo.form.Action = function(form, options){
7089     this.form = form;
7090     this.options = options || {};
7091 };
7092 /**
7093  * Client Validation Failed
7094  * @const 
7095  */
7096 Roo.form.Action.CLIENT_INVALID = 'client';
7097 /**
7098  * Server Validation Failed
7099  * @const 
7100  */
7101 Roo.form.Action.SERVER_INVALID = 'server';
7102  /**
7103  * Connect to Server Failed
7104  * @const 
7105  */
7106 Roo.form.Action.CONNECT_FAILURE = 'connect';
7107 /**
7108  * Reading Data from Server Failed
7109  * @const 
7110  */
7111 Roo.form.Action.LOAD_FAILURE = 'load';
7112
7113 Roo.form.Action.prototype = {
7114     type : 'default',
7115     failureType : undefined,
7116     response : undefined,
7117     result : undefined,
7118
7119     // interface method
7120     run : function(options){
7121
7122     },
7123
7124     // interface method
7125     success : function(response){
7126
7127     },
7128
7129     // interface method
7130     handleResponse : function(response){
7131
7132     },
7133
7134     // default connection failure
7135     failure : function(response){
7136         
7137         this.response = response;
7138         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7139         this.form.afterAction(this, false);
7140     },
7141
7142     processResponse : function(response){
7143         this.response = response;
7144         if(!response.responseText){
7145             return true;
7146         }
7147         this.result = this.handleResponse(response);
7148         return this.result;
7149     },
7150
7151     // utility functions used internally
7152     getUrl : function(appendParams){
7153         var url = this.options.url || this.form.url || this.form.el.dom.action;
7154         if(appendParams){
7155             var p = this.getParams();
7156             if(p){
7157                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7158             }
7159         }
7160         return url;
7161     },
7162
7163     getMethod : function(){
7164         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7165     },
7166
7167     getParams : function(){
7168         var bp = this.form.baseParams;
7169         var p = this.options.params;
7170         if(p){
7171             if(typeof p == "object"){
7172                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7173             }else if(typeof p == 'string' && bp){
7174                 p += '&' + Roo.urlEncode(bp);
7175             }
7176         }else if(bp){
7177             p = Roo.urlEncode(bp);
7178         }
7179         return p;
7180     },
7181
7182     createCallback : function(){
7183         return {
7184             success: this.success,
7185             failure: this.failure,
7186             scope: this,
7187             timeout: (this.form.timeout*1000),
7188             upload: this.form.fileUpload ? this.success : undefined
7189         };
7190     }
7191 };
7192
7193 Roo.form.Action.Submit = function(form, options){
7194     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7195 };
7196
7197 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7198     type : 'submit',
7199
7200     haveProgress : false,
7201     uploadComplete : false,
7202     
7203     // uploadProgress indicator.
7204     uploadProgress : function()
7205     {
7206         if (!this.form.progressUrl) {
7207             return;
7208         }
7209         
7210         if (!this.haveProgress) {
7211             Roo.MessageBox.progress("Uploading", "Uploading");
7212         }
7213         if (this.uploadComplete) {
7214            Roo.MessageBox.hide();
7215            return;
7216         }
7217         
7218         this.haveProgress = true;
7219    
7220         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7221         
7222         var c = new Roo.data.Connection();
7223         c.request({
7224             url : this.form.progressUrl,
7225             params: {
7226                 id : uid
7227             },
7228             method: 'GET',
7229             success : function(req){
7230                //console.log(data);
7231                 var rdata = false;
7232                 var edata;
7233                 try  {
7234                    rdata = Roo.decode(req.responseText)
7235                 } catch (e) {
7236                     Roo.log("Invalid data from server..");
7237                     Roo.log(edata);
7238                     return;
7239                 }
7240                 if (!rdata || !rdata.success) {
7241                     Roo.log(rdata);
7242                     Roo.MessageBox.alert(Roo.encode(rdata));
7243                     return;
7244                 }
7245                 var data = rdata.data;
7246                 
7247                 if (this.uploadComplete) {
7248                    Roo.MessageBox.hide();
7249                    return;
7250                 }
7251                    
7252                 if (data){
7253                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7254                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7255                     );
7256                 }
7257                 this.uploadProgress.defer(2000,this);
7258             },
7259        
7260             failure: function(data) {
7261                 Roo.log('progress url failed ');
7262                 Roo.log(data);
7263             },
7264             scope : this
7265         });
7266            
7267     },
7268     
7269     
7270     run : function()
7271     {
7272         // run get Values on the form, so it syncs any secondary forms.
7273         this.form.getValues();
7274         
7275         var o = this.options;
7276         var method = this.getMethod();
7277         var isPost = method == 'POST';
7278         if(o.clientValidation === false || this.form.isValid()){
7279             
7280             if (this.form.progressUrl) {
7281                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7282                     (new Date() * 1) + '' + Math.random());
7283                     
7284             } 
7285             
7286             
7287             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7288                 form:this.form.el.dom,
7289                 url:this.getUrl(!isPost),
7290                 method: method,
7291                 params:isPost ? this.getParams() : null,
7292                 isUpload: this.form.fileUpload
7293             }));
7294             
7295             this.uploadProgress();
7296
7297         }else if (o.clientValidation !== false){ // client validation failed
7298             this.failureType = Roo.form.Action.CLIENT_INVALID;
7299             this.form.afterAction(this, false);
7300         }
7301     },
7302
7303     success : function(response)
7304     {
7305         this.uploadComplete= true;
7306         if (this.haveProgress) {
7307             Roo.MessageBox.hide();
7308         }
7309         
7310         
7311         var result = this.processResponse(response);
7312         if(result === true || result.success){
7313             this.form.afterAction(this, true);
7314             return;
7315         }
7316         if(result.errors){
7317             this.form.markInvalid(result.errors);
7318             this.failureType = Roo.form.Action.SERVER_INVALID;
7319         }
7320         this.form.afterAction(this, false);
7321     },
7322     failure : function(response)
7323     {
7324         this.uploadComplete= true;
7325         if (this.haveProgress) {
7326             Roo.MessageBox.hide();
7327         }
7328         
7329         this.response = response;
7330         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7331         this.form.afterAction(this, false);
7332     },
7333     
7334     handleResponse : function(response){
7335         if(this.form.errorReader){
7336             var rs = this.form.errorReader.read(response);
7337             var errors = [];
7338             if(rs.records){
7339                 for(var i = 0, len = rs.records.length; i < len; i++) {
7340                     var r = rs.records[i];
7341                     errors[i] = r.data;
7342                 }
7343             }
7344             if(errors.length < 1){
7345                 errors = null;
7346             }
7347             return {
7348                 success : rs.success,
7349                 errors : errors
7350             };
7351         }
7352         var ret = false;
7353         try {
7354             ret = Roo.decode(response.responseText);
7355         } catch (e) {
7356             ret = {
7357                 success: false,
7358                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7359                 errors : []
7360             };
7361         }
7362         return ret;
7363         
7364     }
7365 });
7366
7367
7368 Roo.form.Action.Load = function(form, options){
7369     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7370     this.reader = this.form.reader;
7371 };
7372
7373 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7374     type : 'load',
7375
7376     run : function(){
7377         
7378         Roo.Ajax.request(Roo.apply(
7379                 this.createCallback(), {
7380                     method:this.getMethod(),
7381                     url:this.getUrl(false),
7382                     params:this.getParams()
7383         }));
7384     },
7385
7386     success : function(response){
7387         
7388         var result = this.processResponse(response);
7389         if(result === true || !result.success || !result.data){
7390             this.failureType = Roo.form.Action.LOAD_FAILURE;
7391             this.form.afterAction(this, false);
7392             return;
7393         }
7394         this.form.clearInvalid();
7395         this.form.setValues(result.data);
7396         this.form.afterAction(this, true);
7397     },
7398
7399     handleResponse : function(response){
7400         if(this.form.reader){
7401             var rs = this.form.reader.read(response);
7402             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7403             return {
7404                 success : rs.success,
7405                 data : data
7406             };
7407         }
7408         return Roo.decode(response.responseText);
7409     }
7410 });
7411
7412 Roo.form.Action.ACTION_TYPES = {
7413     'load' : Roo.form.Action.Load,
7414     'submit' : Roo.form.Action.Submit
7415 };/*
7416  * - LGPL
7417  *
7418  * form
7419  *
7420  */
7421
7422 /**
7423  * @class Roo.bootstrap.Form
7424  * @extends Roo.bootstrap.Component
7425  * Bootstrap Form class
7426  * @cfg {String} method  GET | POST (default POST)
7427  * @cfg {String} labelAlign top | left (default top)
7428  * @cfg {String} align left  | right - for navbars
7429  * @cfg {Boolean} loadMask load mask when submit (default true)
7430
7431  *
7432  * @constructor
7433  * Create a new Form
7434  * @param {Object} config The config object
7435  */
7436
7437
7438 Roo.bootstrap.Form = function(config){
7439     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7440     this.addEvents({
7441         /**
7442          * @event clientvalidation
7443          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7444          * @param {Form} this
7445          * @param {Boolean} valid true if the form has passed client-side validation
7446          */
7447         clientvalidation: true,
7448         /**
7449          * @event beforeaction
7450          * Fires before any action is performed. Return false to cancel the action.
7451          * @param {Form} this
7452          * @param {Action} action The action to be performed
7453          */
7454         beforeaction: true,
7455         /**
7456          * @event actionfailed
7457          * Fires when an action fails.
7458          * @param {Form} this
7459          * @param {Action} action The action that failed
7460          */
7461         actionfailed : true,
7462         /**
7463          * @event actioncomplete
7464          * Fires when an action is completed.
7465          * @param {Form} this
7466          * @param {Action} action The action that completed
7467          */
7468         actioncomplete : true
7469     });
7470
7471 };
7472
7473 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7474
7475      /**
7476      * @cfg {String} method
7477      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7478      */
7479     method : 'POST',
7480     /**
7481      * @cfg {String} url
7482      * The URL to use for form actions if one isn't supplied in the action options.
7483      */
7484     /**
7485      * @cfg {Boolean} fileUpload
7486      * Set to true if this form is a file upload.
7487      */
7488
7489     /**
7490      * @cfg {Object} baseParams
7491      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7492      */
7493
7494     /**
7495      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7496      */
7497     timeout: 30,
7498     /**
7499      * @cfg {Sting} align (left|right) for navbar forms
7500      */
7501     align : 'left',
7502
7503     // private
7504     activeAction : null,
7505
7506     /**
7507      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7508      * element by passing it or its id or mask the form itself by passing in true.
7509      * @type Mixed
7510      */
7511     waitMsgTarget : false,
7512
7513     loadMask : true,
7514
7515     getAutoCreate : function(){
7516
7517         var cfg = {
7518             tag: 'form',
7519             method : this.method || 'POST',
7520             id : this.id || Roo.id(),
7521             cls : ''
7522         };
7523         if (this.parent().xtype.match(/^Nav/)) {
7524             cfg.cls = 'navbar-form navbar-' + this.align;
7525
7526         }
7527
7528         if (this.labelAlign == 'left' ) {
7529             cfg.cls += ' form-horizontal';
7530         }
7531
7532
7533         return cfg;
7534     },
7535     initEvents : function()
7536     {
7537         this.el.on('submit', this.onSubmit, this);
7538         // this was added as random key presses on the form where triggering form submit.
7539         this.el.on('keypress', function(e) {
7540             if (e.getCharCode() != 13) {
7541                 return true;
7542             }
7543             // we might need to allow it for textareas.. and some other items.
7544             // check e.getTarget().
7545
7546             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7547                 return true;
7548             }
7549
7550             Roo.log("keypress blocked");
7551
7552             e.preventDefault();
7553             return false;
7554         });
7555
7556     },
7557     // private
7558     onSubmit : function(e){
7559         e.stopEvent();
7560     },
7561
7562      /**
7563      * Returns true if client-side validation on the form is successful.
7564      * @return Boolean
7565      */
7566     isValid : function(){
7567         var items = this.getItems();
7568         var valid = true;
7569         items.each(function(f){
7570            if(!f.validate()){
7571                valid = false;
7572
7573            }
7574         });
7575         return valid;
7576     },
7577     /**
7578      * Returns true if any fields in this form have changed since their original load.
7579      * @return Boolean
7580      */
7581     isDirty : function(){
7582         var dirty = false;
7583         var items = this.getItems();
7584         items.each(function(f){
7585            if(f.isDirty()){
7586                dirty = true;
7587                return false;
7588            }
7589            return true;
7590         });
7591         return dirty;
7592     },
7593      /**
7594      * Performs a predefined action (submit or load) or custom actions you define on this form.
7595      * @param {String} actionName The name of the action type
7596      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7597      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7598      * accept other config options):
7599      * <pre>
7600 Property          Type             Description
7601 ----------------  ---------------  ----------------------------------------------------------------------------------
7602 url               String           The url for the action (defaults to the form's url)
7603 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7604 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7605 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7606                                    validate the form on the client (defaults to false)
7607      * </pre>
7608      * @return {BasicForm} this
7609      */
7610     doAction : function(action, options){
7611         if(typeof action == 'string'){
7612             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7613         }
7614         if(this.fireEvent('beforeaction', this, action) !== false){
7615             this.beforeAction(action);
7616             action.run.defer(100, action);
7617         }
7618         return this;
7619     },
7620
7621     // private
7622     beforeAction : function(action){
7623         var o = action.options;
7624
7625         if(this.loadMask){
7626             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7627         }
7628         // not really supported yet.. ??
7629
7630         //if(this.waitMsgTarget === true){
7631         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7632         //}else if(this.waitMsgTarget){
7633         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7634         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7635         //}else {
7636         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7637        // }
7638
7639     },
7640
7641     // private
7642     afterAction : function(action, success){
7643         this.activeAction = null;
7644         var o = action.options;
7645
7646         //if(this.waitMsgTarget === true){
7647             this.el.unmask();
7648         //}else if(this.waitMsgTarget){
7649         //    this.waitMsgTarget.unmask();
7650         //}else{
7651         //    Roo.MessageBox.updateProgress(1);
7652         //    Roo.MessageBox.hide();
7653        // }
7654         //
7655         if(success){
7656             if(o.reset){
7657                 this.reset();
7658             }
7659             Roo.callback(o.success, o.scope, [this, action]);
7660             this.fireEvent('actioncomplete', this, action);
7661
7662         }else{
7663
7664             // failure condition..
7665             // we have a scenario where updates need confirming.
7666             // eg. if a locking scenario exists..
7667             // we look for { errors : { needs_confirm : true }} in the response.
7668             if (
7669                 (typeof(action.result) != 'undefined')  &&
7670                 (typeof(action.result.errors) != 'undefined')  &&
7671                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7672            ){
7673                 var _t = this;
7674                 Roo.log("not supported yet");
7675                  /*
7676
7677                 Roo.MessageBox.confirm(
7678                     "Change requires confirmation",
7679                     action.result.errorMsg,
7680                     function(r) {
7681                         if (r != 'yes') {
7682                             return;
7683                         }
7684                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7685                     }
7686
7687                 );
7688                 */
7689
7690
7691                 return;
7692             }
7693
7694             Roo.callback(o.failure, o.scope, [this, action]);
7695             // show an error message if no failed handler is set..
7696             if (!this.hasListener('actionfailed')) {
7697                 Roo.log("need to add dialog support");
7698                 /*
7699                 Roo.MessageBox.alert("Error",
7700                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7701                         action.result.errorMsg :
7702                         "Saving Failed, please check your entries or try again"
7703                 );
7704                 */
7705             }
7706
7707             this.fireEvent('actionfailed', this, action);
7708         }
7709
7710     },
7711     /**
7712      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7713      * @param {String} id The value to search for
7714      * @return Field
7715      */
7716     findField : function(id){
7717         var items = this.getItems();
7718         var field = items.get(id);
7719         if(!field){
7720              items.each(function(f){
7721                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7722                     field = f;
7723                     return false;
7724                 }
7725                 return true;
7726             });
7727         }
7728         return field || null;
7729     },
7730      /**
7731      * Mark fields in this form invalid in bulk.
7732      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7733      * @return {BasicForm} this
7734      */
7735     markInvalid : function(errors){
7736         if(errors instanceof Array){
7737             for(var i = 0, len = errors.length; i < len; i++){
7738                 var fieldError = errors[i];
7739                 var f = this.findField(fieldError.id);
7740                 if(f){
7741                     f.markInvalid(fieldError.msg);
7742                 }
7743             }
7744         }else{
7745             var field, id;
7746             for(id in errors){
7747                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7748                     field.markInvalid(errors[id]);
7749                 }
7750             }
7751         }
7752         //Roo.each(this.childForms || [], function (f) {
7753         //    f.markInvalid(errors);
7754         //});
7755
7756         return this;
7757     },
7758
7759     /**
7760      * Set values for fields in this form in bulk.
7761      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7762      * @return {BasicForm} this
7763      */
7764     setValues : function(values){
7765         if(values instanceof Array){ // array of objects
7766             for(var i = 0, len = values.length; i < len; i++){
7767                 var v = values[i];
7768                 var f = this.findField(v.id);
7769                 if(f){
7770                     f.setValue(v.value);
7771                     if(this.trackResetOnLoad){
7772                         f.originalValue = f.getValue();
7773                     }
7774                 }
7775             }
7776         }else{ // object hash
7777             var field, id;
7778             for(id in values){
7779                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7780
7781                     if (field.setFromData &&
7782                         field.valueField &&
7783                         field.displayField &&
7784                         // combos' with local stores can
7785                         // be queried via setValue()
7786                         // to set their value..
7787                         (field.store && !field.store.isLocal)
7788                         ) {
7789                         // it's a combo
7790                         var sd = { };
7791                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7792                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7793                         field.setFromData(sd);
7794
7795                     } else {
7796                         field.setValue(values[id]);
7797                     }
7798
7799
7800                     if(this.trackResetOnLoad){
7801                         field.originalValue = field.getValue();
7802                     }
7803                 }
7804             }
7805         }
7806
7807         //Roo.each(this.childForms || [], function (f) {
7808         //    f.setValues(values);
7809         //});
7810
7811         return this;
7812     },
7813
7814     /**
7815      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7816      * they are returned as an array.
7817      * @param {Boolean} asString
7818      * @return {Object}
7819      */
7820     getValues : function(asString){
7821         //if (this.childForms) {
7822             // copy values from the child forms
7823         //    Roo.each(this.childForms, function (f) {
7824         //        this.setValues(f.getValues());
7825         //    }, this);
7826         //}
7827
7828
7829
7830         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7831         if(asString === true){
7832             return fs;
7833         }
7834         return Roo.urlDecode(fs);
7835     },
7836
7837     /**
7838      * Returns the fields in this form as an object with key/value pairs.
7839      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7840      * @return {Object}
7841      */
7842     getFieldValues : function(with_hidden)
7843     {
7844         var items = this.getItems();
7845         var ret = {};
7846         items.each(function(f){
7847             if (!f.getName()) {
7848                 return;
7849             }
7850             var v = f.getValue();
7851             if (f.inputType =='radio') {
7852                 if (typeof(ret[f.getName()]) == 'undefined') {
7853                     ret[f.getName()] = ''; // empty..
7854                 }
7855
7856                 if (!f.el.dom.checked) {
7857                     return;
7858
7859                 }
7860                 v = f.el.dom.value;
7861
7862             }
7863
7864             // not sure if this supported any more..
7865             if ((typeof(v) == 'object') && f.getRawValue) {
7866                 v = f.getRawValue() ; // dates..
7867             }
7868             // combo boxes where name != hiddenName...
7869             if (f.name != f.getName()) {
7870                 ret[f.name] = f.getRawValue();
7871             }
7872             ret[f.getName()] = v;
7873         });
7874
7875         return ret;
7876     },
7877
7878     /**
7879      * Clears all invalid messages in this form.
7880      * @return {BasicForm} this
7881      */
7882     clearInvalid : function(){
7883         var items = this.getItems();
7884
7885         items.each(function(f){
7886            f.clearInvalid();
7887         });
7888
7889
7890
7891         return this;
7892     },
7893
7894     /**
7895      * Resets this form.
7896      * @return {BasicForm} this
7897      */
7898     reset : function(){
7899         var items = this.getItems();
7900         items.each(function(f){
7901             f.reset();
7902         });
7903
7904         Roo.each(this.childForms || [], function (f) {
7905             f.reset();
7906         });
7907
7908
7909         return this;
7910     },
7911     getItems : function()
7912     {
7913         var r=new Roo.util.MixedCollection(false, function(o){
7914             return o.id || (o.id = Roo.id());
7915         });
7916         var iter = function(el) {
7917             if (el.inputEl) {
7918                 r.add(el);
7919             }
7920             if (!el.items) {
7921                 return;
7922             }
7923             Roo.each(el.items,function(e) {
7924                 iter(e);
7925             });
7926
7927
7928         };
7929
7930         iter(this);
7931         return r;
7932
7933
7934
7935
7936     }
7937
7938 });
7939 /*
7940  * Based on:
7941  * Ext JS Library 1.1.1
7942  * Copyright(c) 2006-2007, Ext JS, LLC.
7943  *
7944  * Originally Released Under LGPL - original licence link has changed is not relivant.
7945  *
7946  * Fork - LGPL
7947  * <script type="text/javascript">
7948  */
7949 /**
7950  * @class Roo.form.VTypes
7951  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7952  * @singleton
7953  */
7954 Roo.form.VTypes = function(){
7955     // closure these in so they are only created once.
7956     var alpha = /^[a-zA-Z_]+$/;
7957     var alphanum = /^[a-zA-Z0-9_]+$/;
7958     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7959     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7960
7961     // All these messages and functions are configurable
7962     return {
7963         /**
7964          * The function used to validate email addresses
7965          * @param {String} value The email address
7966          */
7967         'email' : function(v){
7968             return email.test(v);
7969         },
7970         /**
7971          * The error text to display when the email validation function returns false
7972          * @type String
7973          */
7974         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7975         /**
7976          * The keystroke filter mask to be applied on email input
7977          * @type RegExp
7978          */
7979         'emailMask' : /[a-z0-9_\.\-@]/i,
7980
7981         /**
7982          * The function used to validate URLs
7983          * @param {String} value The URL
7984          */
7985         'url' : function(v){
7986             return url.test(v);
7987         },
7988         /**
7989          * The error text to display when the url validation function returns false
7990          * @type String
7991          */
7992         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7993         
7994         /**
7995          * The function used to validate alpha values
7996          * @param {String} value The value
7997          */
7998         'alpha' : function(v){
7999             return alpha.test(v);
8000         },
8001         /**
8002          * The error text to display when the alpha validation function returns false
8003          * @type String
8004          */
8005         'alphaText' : 'This field should only contain letters and _',
8006         /**
8007          * The keystroke filter mask to be applied on alpha input
8008          * @type RegExp
8009          */
8010         'alphaMask' : /[a-z_]/i,
8011
8012         /**
8013          * The function used to validate alphanumeric values
8014          * @param {String} value The value
8015          */
8016         'alphanum' : function(v){
8017             return alphanum.test(v);
8018         },
8019         /**
8020          * The error text to display when the alphanumeric validation function returns false
8021          * @type String
8022          */
8023         'alphanumText' : 'This field should only contain letters, numbers and _',
8024         /**
8025          * The keystroke filter mask to be applied on alphanumeric input
8026          * @type RegExp
8027          */
8028         'alphanumMask' : /[a-z0-9_]/i
8029     };
8030 }();/*
8031  * - LGPL
8032  *
8033  * Input
8034  * 
8035  */
8036
8037 /**
8038  * @class Roo.bootstrap.Input
8039  * @extends Roo.bootstrap.Component
8040  * Bootstrap Input class
8041  * @cfg {Boolean} disabled is it disabled
8042  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8043  * @cfg {String} name name of the input
8044  * @cfg {string} fieldLabel - the label associated
8045  * @cfg {string} placeholder - placeholder to put in text.
8046  * @cfg {string}  before - input group add on before
8047  * @cfg {string} after - input group add on after
8048  * @cfg {string} size - (lg|sm) or leave empty..
8049  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8050  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8051  * @cfg {Number} md colspan out of 12 for computer-sized screens
8052  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8053  * @cfg {string} value default value of the input
8054  * @cfg {Number} labelWidth set the width of label (0-12)
8055  * @cfg {String} labelAlign (top|left)
8056  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8057  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8058  * @cfg {String} indicatorpos (left|right) default left
8059
8060  * @cfg {String} align (left|center|right) Default left
8061  * @cfg {Boolean} forceFeedback (true|false) Default false
8062  * 
8063  * 
8064  * 
8065  * 
8066  * @constructor
8067  * Create a new Input
8068  * @param {Object} config The config object
8069  */
8070
8071 Roo.bootstrap.Input = function(config){
8072     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8073    
8074         this.addEvents({
8075             /**
8076              * @event focus
8077              * Fires when this field receives input focus.
8078              * @param {Roo.form.Field} this
8079              */
8080             focus : true,
8081             /**
8082              * @event blur
8083              * Fires when this field loses input focus.
8084              * @param {Roo.form.Field} this
8085              */
8086             blur : true,
8087             /**
8088              * @event specialkey
8089              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8090              * {@link Roo.EventObject#getKey} to determine which key was pressed.
8091              * @param {Roo.form.Field} this
8092              * @param {Roo.EventObject} e The event object
8093              */
8094             specialkey : true,
8095             /**
8096              * @event change
8097              * Fires just before the field blurs if the field value has changed.
8098              * @param {Roo.form.Field} this
8099              * @param {Mixed} newValue The new value
8100              * @param {Mixed} oldValue The original value
8101              */
8102             change : true,
8103             /**
8104              * @event invalid
8105              * Fires after the field has been marked as invalid.
8106              * @param {Roo.form.Field} this
8107              * @param {String} msg The validation message
8108              */
8109             invalid : true,
8110             /**
8111              * @event valid
8112              * Fires after the field has been validated with no errors.
8113              * @param {Roo.form.Field} this
8114              */
8115             valid : true,
8116              /**
8117              * @event keyup
8118              * Fires after the key up
8119              * @param {Roo.form.Field} this
8120              * @param {Roo.EventObject}  e The event Object
8121              */
8122             keyup : true
8123         });
8124 };
8125
8126 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8127      /**
8128      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8129       automatic validation (defaults to "keyup").
8130      */
8131     validationEvent : "keyup",
8132      /**
8133      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8134      */
8135     validateOnBlur : true,
8136     /**
8137      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8138      */
8139     validationDelay : 250,
8140      /**
8141      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8142      */
8143     focusClass : "x-form-focus",  // not needed???
8144     
8145        
8146     /**
8147      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8148      */
8149     invalidClass : "has-warning",
8150     
8151     /**
8152      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8153      */
8154     validClass : "has-success",
8155     
8156     /**
8157      * @cfg {Boolean} hasFeedback (true|false) default true
8158      */
8159     hasFeedback : true,
8160     
8161     /**
8162      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8163      */
8164     invalidFeedbackClass : "glyphicon-warning-sign",
8165     
8166     /**
8167      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8168      */
8169     validFeedbackClass : "glyphicon-ok",
8170     
8171     /**
8172      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8173      */
8174     selectOnFocus : false,
8175     
8176      /**
8177      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8178      */
8179     maskRe : null,
8180        /**
8181      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8182      */
8183     vtype : null,
8184     
8185       /**
8186      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8187      */
8188     disableKeyFilter : false,
8189     
8190        /**
8191      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8192      */
8193     disabled : false,
8194      /**
8195      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8196      */
8197     allowBlank : true,
8198     /**
8199      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8200      */
8201     blankText : "This field is required",
8202     
8203      /**
8204      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8205      */
8206     minLength : 0,
8207     /**
8208      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8209      */
8210     maxLength : Number.MAX_VALUE,
8211     /**
8212      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8213      */
8214     minLengthText : "The minimum length for this field is {0}",
8215     /**
8216      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8217      */
8218     maxLengthText : "The maximum length for this field is {0}",
8219   
8220     
8221     /**
8222      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8223      * If available, this function will be called only after the basic validators all return true, and will be passed the
8224      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8225      */
8226     validator : null,
8227     /**
8228      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8229      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8230      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8231      */
8232     regex : null,
8233     /**
8234      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8235      */
8236     regexText : "",
8237     
8238     autocomplete: false,
8239     
8240     
8241     fieldLabel : '',
8242     inputType : 'text',
8243     
8244     name : false,
8245     placeholder: false,
8246     before : false,
8247     after : false,
8248     size : false,
8249     hasFocus : false,
8250     preventMark: false,
8251     isFormField : true,
8252     value : '',
8253     labelWidth : 2,
8254     labelAlign : false,
8255     readOnly : false,
8256     align : false,
8257     formatedValue : false,
8258     forceFeedback : false,
8259     
8260     indicatorpos : 'left',
8261     
8262     parentLabelAlign : function()
8263     {
8264         var parent = this;
8265         while (parent.parent()) {
8266             parent = parent.parent();
8267             if (typeof(parent.labelAlign) !='undefined') {
8268                 return parent.labelAlign;
8269             }
8270         }
8271         return 'left';
8272         
8273     },
8274     
8275     getAutoCreate : function()
8276     {
8277         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8278         
8279         var id = Roo.id();
8280         
8281         var cfg = {};
8282         
8283         if(this.inputType != 'hidden'){
8284             cfg.cls = 'form-group' //input-group
8285         }
8286         
8287         var input =  {
8288             tag: 'input',
8289             id : id,
8290             type : this.inputType,
8291             value : this.value,
8292             cls : 'form-control',
8293             placeholder : this.placeholder || '',
8294             autocomplete : this.autocomplete || 'new-password'
8295         };
8296         
8297         if(this.align){
8298             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8299         }
8300         
8301         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8302             input.maxLength = this.maxLength;
8303         }
8304         
8305         if (this.disabled) {
8306             input.disabled=true;
8307         }
8308         
8309         if (this.readOnly) {
8310             input.readonly=true;
8311         }
8312         
8313         if (this.name) {
8314             input.name = this.name;
8315         }
8316         
8317         if (this.size) {
8318             input.cls += ' input-' + this.size;
8319         }
8320         
8321         var settings=this;
8322         ['xs','sm','md','lg'].map(function(size){
8323             if (settings[size]) {
8324                 cfg.cls += ' col-' + size + '-' + settings[size];
8325             }
8326         });
8327         
8328         var inputblock = input;
8329         
8330         var feedback = {
8331             tag: 'span',
8332             cls: 'glyphicon form-control-feedback'
8333         };
8334             
8335         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8336             
8337             inputblock = {
8338                 cls : 'has-feedback',
8339                 cn :  [
8340                     input,
8341                     feedback
8342                 ] 
8343             };  
8344         }
8345         
8346         if (this.before || this.after) {
8347             
8348             inputblock = {
8349                 cls : 'input-group',
8350                 cn :  [] 
8351             };
8352             
8353             if (this.before && typeof(this.before) == 'string') {
8354                 
8355                 inputblock.cn.push({
8356                     tag :'span',
8357                     cls : 'roo-input-before input-group-addon',
8358                     html : this.before
8359                 });
8360             }
8361             if (this.before && typeof(this.before) == 'object') {
8362                 this.before = Roo.factory(this.before);
8363                 
8364                 inputblock.cn.push({
8365                     tag :'span',
8366                     cls : 'roo-input-before input-group-' +
8367                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8368                 });
8369             }
8370             
8371             inputblock.cn.push(input);
8372             
8373             if (this.after && typeof(this.after) == 'string') {
8374                 inputblock.cn.push({
8375                     tag :'span',
8376                     cls : 'roo-input-after input-group-addon',
8377                     html : this.after
8378                 });
8379             }
8380             if (this.after && typeof(this.after) == 'object') {
8381                 this.after = Roo.factory(this.after);
8382                 
8383                 inputblock.cn.push({
8384                     tag :'span',
8385                     cls : 'roo-input-after input-group-' +
8386                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8387                 });
8388             }
8389             
8390             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8391                 inputblock.cls += ' has-feedback';
8392                 inputblock.cn.push(feedback);
8393             }
8394         };
8395         
8396         if (align ==='left' && this.fieldLabel.length) {
8397             
8398             cfg.cn = [
8399                 {
8400                     tag : 'i',
8401                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8402                     tooltip : 'This field is required'
8403                 },
8404                 {
8405                     tag: 'label',
8406                     'for' :  id,
8407                     cls : 'control-label col-sm-' + this.labelWidth,
8408                     html : this.fieldLabel
8409
8410                 },
8411                 {
8412                     cls : "col-sm-" + (12 - this.labelWidth), 
8413                     cn: [
8414                         inputblock
8415                     ]
8416                 }
8417
8418             ];
8419             
8420             if(this.indicatorpos == 'right'){
8421                 cfg.cn = [
8422                     {
8423                         tag: 'label',
8424                         'for' :  id,
8425                         cls : 'control-label col-sm-' + this.labelWidth,
8426                         html : this.fieldLabel
8427
8428                     },
8429                     {
8430                         tag : 'i',
8431                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8432                         tooltip : 'This field is required'
8433                     },
8434                     {
8435                         cls : "col-sm-" + (12 - this.labelWidth), 
8436                         cn: [
8437                             inputblock
8438                         ]
8439                     }
8440
8441                 ];
8442             }
8443             
8444         } else if ( this.fieldLabel.length) {
8445                 
8446             cfg.cn = [
8447                 {
8448                     tag : 'i',
8449                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8450                     tooltip : 'This field is required'
8451                 },
8452                 {
8453                     tag: 'label',
8454                    //cls : 'input-group-addon',
8455                     html : this.fieldLabel
8456
8457                 },
8458
8459                inputblock
8460
8461            ];
8462            
8463            if(this.indicatorpos == 'right'){
8464                 
8465                 cfg.cn = [
8466                     {
8467                         tag: 'label',
8468                        //cls : 'input-group-addon',
8469                         html : this.fieldLabel
8470
8471                     },
8472                     {
8473                         tag : 'i',
8474                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8475                         tooltip : 'This field is required'
8476                     },
8477
8478                    inputblock
8479
8480                ];
8481
8482             }
8483
8484         } else {
8485             
8486             cfg.cn = [
8487
8488                     inputblock
8489
8490             ];
8491                 
8492                 
8493         };
8494         
8495         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8496            cfg.cls += ' navbar-form';
8497         }
8498         
8499         if (this.parentType === 'NavGroup') {
8500            cfg.cls += ' navbar-form';
8501            cfg.tag = 'li';
8502         }
8503         
8504         return cfg;
8505         
8506     },
8507     /**
8508      * return the real input element.
8509      */
8510     inputEl: function ()
8511     {
8512         return this.el.select('input.form-control',true).first();
8513     },
8514     
8515     tooltipEl : function()
8516     {
8517         return this.inputEl();
8518     },
8519     
8520     indicatorEl : function()
8521     {
8522         var indicator = this.el.select('i.roo-required-indicator',true).first();
8523         
8524         if(!indicator){
8525             return false;
8526         }
8527         
8528         return indicator;
8529         
8530     },
8531     
8532     setDisabled : function(v)
8533     {
8534         var i  = this.inputEl().dom;
8535         if (!v) {
8536             i.removeAttribute('disabled');
8537             return;
8538             
8539         }
8540         i.setAttribute('disabled','true');
8541     },
8542     initEvents : function()
8543     {
8544           
8545         this.inputEl().on("keydown" , this.fireKey,  this);
8546         this.inputEl().on("focus", this.onFocus,  this);
8547         this.inputEl().on("blur", this.onBlur,  this);
8548         
8549         this.inputEl().relayEvent('keyup', this);
8550         
8551         this.indicator = this.indicatorEl();
8552         
8553         if(this.indicator){
8554             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8555             this.indicator.hide();
8556         }
8557  
8558         // reference to original value for reset
8559         this.originalValue = this.getValue();
8560         //Roo.form.TextField.superclass.initEvents.call(this);
8561         if(this.validationEvent == 'keyup'){
8562             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8563             this.inputEl().on('keyup', this.filterValidation, this);
8564         }
8565         else if(this.validationEvent !== false){
8566             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8567         }
8568         
8569         if(this.selectOnFocus){
8570             this.on("focus", this.preFocus, this);
8571             
8572         }
8573         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8574             this.inputEl().on("keypress", this.filterKeys, this);
8575         } else {
8576             this.inputEl().relayEvent('keypress', this);
8577         }
8578        /* if(this.grow){
8579             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8580             this.el.on("click", this.autoSize,  this);
8581         }
8582         */
8583         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8584             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8585         }
8586         
8587         if (typeof(this.before) == 'object') {
8588             this.before.render(this.el.select('.roo-input-before',true).first());
8589         }
8590         if (typeof(this.after) == 'object') {
8591             this.after.render(this.el.select('.roo-input-after',true).first());
8592         }
8593         
8594         
8595     },
8596     filterValidation : function(e){
8597         if(!e.isNavKeyPress()){
8598             this.validationTask.delay(this.validationDelay);
8599         }
8600     },
8601      /**
8602      * Validates the field value
8603      * @return {Boolean} True if the value is valid, else false
8604      */
8605     validate : function(){
8606         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8607         if(this.disabled || this.validateValue(this.getRawValue())){
8608             this.markValid();
8609             return true;
8610         }
8611         
8612         this.markInvalid();
8613         return false;
8614     },
8615     
8616     
8617     /**
8618      * Validates a value according to the field's validation rules and marks the field as invalid
8619      * if the validation fails
8620      * @param {Mixed} value The value to validate
8621      * @return {Boolean} True if the value is valid, else false
8622      */
8623     validateValue : function(value){
8624         if(value.length < 1)  { // if it's blank
8625             if(this.allowBlank){
8626                 return true;
8627             }
8628             return false;
8629         }
8630         
8631         if(value.length < this.minLength){
8632             return false;
8633         }
8634         if(value.length > this.maxLength){
8635             return false;
8636         }
8637         if(this.vtype){
8638             var vt = Roo.form.VTypes;
8639             if(!vt[this.vtype](value, this)){
8640                 return false;
8641             }
8642         }
8643         if(typeof this.validator == "function"){
8644             var msg = this.validator(value);
8645             if(msg !== true){
8646                 return false;
8647             }
8648         }
8649         
8650         if(this.regex && !this.regex.test(value)){
8651             return false;
8652         }
8653         
8654         return true;
8655     },
8656
8657     
8658     
8659      // private
8660     fireKey : function(e){
8661         //Roo.log('field ' + e.getKey());
8662         if(e.isNavKeyPress()){
8663             this.fireEvent("specialkey", this, e);
8664         }
8665     },
8666     focus : function (selectText){
8667         if(this.rendered){
8668             this.inputEl().focus();
8669             if(selectText === true){
8670                 this.inputEl().dom.select();
8671             }
8672         }
8673         return this;
8674     } ,
8675     
8676     onFocus : function(){
8677         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8678            // this.el.addClass(this.focusClass);
8679         }
8680         if(!this.hasFocus){
8681             this.hasFocus = true;
8682             this.startValue = this.getValue();
8683             this.fireEvent("focus", this);
8684         }
8685     },
8686     
8687     beforeBlur : Roo.emptyFn,
8688
8689     
8690     // private
8691     onBlur : function(){
8692         this.beforeBlur();
8693         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8694             //this.el.removeClass(this.focusClass);
8695         }
8696         this.hasFocus = false;
8697         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8698             this.validate();
8699         }
8700         var v = this.getValue();
8701         if(String(v) !== String(this.startValue)){
8702             this.fireEvent('change', this, v, this.startValue);
8703         }
8704         this.fireEvent("blur", this);
8705     },
8706     
8707     /**
8708      * Resets the current field value to the originally loaded value and clears any validation messages
8709      */
8710     reset : function(){
8711         this.setValue(this.originalValue);
8712         this.validate();
8713     },
8714      /**
8715      * Returns the name of the field
8716      * @return {Mixed} name The name field
8717      */
8718     getName: function(){
8719         return this.name;
8720     },
8721      /**
8722      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8723      * @return {Mixed} value The field value
8724      */
8725     getValue : function(){
8726         
8727         var v = this.inputEl().getValue();
8728         
8729         return v;
8730     },
8731     /**
8732      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8733      * @return {Mixed} value The field value
8734      */
8735     getRawValue : function(){
8736         var v = this.inputEl().getValue();
8737         
8738         return v;
8739     },
8740     
8741     /**
8742      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8743      * @param {Mixed} value The value to set
8744      */
8745     setRawValue : function(v){
8746         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8747     },
8748     
8749     selectText : function(start, end){
8750         var v = this.getRawValue();
8751         if(v.length > 0){
8752             start = start === undefined ? 0 : start;
8753             end = end === undefined ? v.length : end;
8754             var d = this.inputEl().dom;
8755             if(d.setSelectionRange){
8756                 d.setSelectionRange(start, end);
8757             }else if(d.createTextRange){
8758                 var range = d.createTextRange();
8759                 range.moveStart("character", start);
8760                 range.moveEnd("character", v.length-end);
8761                 range.select();
8762             }
8763         }
8764     },
8765     
8766     /**
8767      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8768      * @param {Mixed} value The value to set
8769      */
8770     setValue : function(v){
8771         this.value = v;
8772         if(this.rendered){
8773             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8774             this.validate();
8775         }
8776     },
8777     
8778     /*
8779     processValue : function(value){
8780         if(this.stripCharsRe){
8781             var newValue = value.replace(this.stripCharsRe, '');
8782             if(newValue !== value){
8783                 this.setRawValue(newValue);
8784                 return newValue;
8785             }
8786         }
8787         return value;
8788     },
8789   */
8790     preFocus : function(){
8791         
8792         if(this.selectOnFocus){
8793             this.inputEl().dom.select();
8794         }
8795     },
8796     filterKeys : function(e){
8797         var k = e.getKey();
8798         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8799             return;
8800         }
8801         var c = e.getCharCode(), cc = String.fromCharCode(c);
8802         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8803             return;
8804         }
8805         if(!this.maskRe.test(cc)){
8806             e.stopEvent();
8807         }
8808     },
8809      /**
8810      * Clear any invalid styles/messages for this field
8811      */
8812     clearInvalid : function(){
8813         
8814         if(!this.el || this.preventMark){ // not rendered
8815             return;
8816         }
8817         
8818         if(this.indicator){
8819             this.indicator.hide();
8820         }
8821         
8822         this.el.removeClass(this.invalidClass);
8823         
8824         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8825             
8826             var feedback = this.el.select('.form-control-feedback', true).first();
8827             
8828             if(feedback){
8829                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8830             }
8831             
8832         }
8833         
8834         this.fireEvent('valid', this);
8835     },
8836     
8837      /**
8838      * Mark this field as valid
8839      */
8840     markValid : function()
8841     {
8842         if(!this.el  || this.preventMark){ // not rendered
8843             return;
8844         }
8845         
8846         this.el.removeClass([this.invalidClass, this.validClass]);
8847         
8848         var feedback = this.el.select('.form-control-feedback', true).first();
8849             
8850         if(feedback){
8851             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8852         }
8853
8854         if(this.disabled){
8855             return;
8856         }
8857         
8858         if(this.allowBlank && !this.getRawValue().length){
8859             return;
8860         }
8861         
8862         if(this.indicator){
8863             this.indicator.hide();
8864         }
8865         
8866         this.el.addClass(this.validClass);
8867         
8868         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8869             
8870             var feedback = this.el.select('.form-control-feedback', true).first();
8871             
8872             if(feedback){
8873                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8874                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8875             }
8876             
8877         }
8878         
8879         this.fireEvent('valid', this);
8880     },
8881     
8882      /**
8883      * Mark this field as invalid
8884      * @param {String} msg The validation message
8885      */
8886     markInvalid : function(msg)
8887     {
8888         if(!this.el  || this.preventMark){ // not rendered
8889             return;
8890         }
8891         
8892         this.el.removeClass([this.invalidClass, this.validClass]);
8893         
8894         var feedback = this.el.select('.form-control-feedback', true).first();
8895             
8896         if(feedback){
8897             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8898         }
8899
8900         if(this.disabled){
8901             return;
8902         }
8903         
8904         if(this.allowBlank && !this.getRawValue().length){
8905             return;
8906         }
8907         
8908         if(this.indicator){
8909             this.indicator.show();
8910         }
8911         
8912         this.el.addClass(this.invalidClass);
8913         
8914         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8915             
8916             var feedback = this.el.select('.form-control-feedback', true).first();
8917             
8918             if(feedback){
8919                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8920                 
8921                 if(this.getValue().length || this.forceFeedback){
8922                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8923                 }
8924                 
8925             }
8926             
8927         }
8928         
8929         this.fireEvent('invalid', this, msg);
8930     },
8931     // private
8932     SafariOnKeyDown : function(event)
8933     {
8934         // this is a workaround for a password hang bug on chrome/ webkit.
8935         if (this.inputEl().dom.type != 'password') {
8936             return;
8937         }
8938         
8939         var isSelectAll = false;
8940         
8941         if(this.inputEl().dom.selectionEnd > 0){
8942             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8943         }
8944         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8945             event.preventDefault();
8946             this.setValue('');
8947             return;
8948         }
8949         
8950         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
8951             
8952             event.preventDefault();
8953             // this is very hacky as keydown always get's upper case.
8954             //
8955             var cc = String.fromCharCode(event.getCharCode());
8956             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8957             
8958         }
8959     },
8960     adjustWidth : function(tag, w){
8961         tag = tag.toLowerCase();
8962         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8963             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8964                 if(tag == 'input'){
8965                     return w + 2;
8966                 }
8967                 if(tag == 'textarea'){
8968                     return w-2;
8969                 }
8970             }else if(Roo.isOpera){
8971                 if(tag == 'input'){
8972                     return w + 2;
8973                 }
8974                 if(tag == 'textarea'){
8975                     return w-2;
8976                 }
8977             }
8978         }
8979         return w;
8980     }
8981     
8982 });
8983
8984  
8985 /*
8986  * - LGPL
8987  *
8988  * Input
8989  * 
8990  */
8991
8992 /**
8993  * @class Roo.bootstrap.TextArea
8994  * @extends Roo.bootstrap.Input
8995  * Bootstrap TextArea class
8996  * @cfg {Number} cols Specifies the visible width of a text area
8997  * @cfg {Number} rows Specifies the visible number of lines in a text area
8998  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8999  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9000  * @cfg {string} html text
9001  * 
9002  * @constructor
9003  * Create a new TextArea
9004  * @param {Object} config The config object
9005  */
9006
9007 Roo.bootstrap.TextArea = function(config){
9008     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9009    
9010 };
9011
9012 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9013      
9014     cols : false,
9015     rows : 5,
9016     readOnly : false,
9017     warp : 'soft',
9018     resize : false,
9019     value: false,
9020     html: false,
9021     
9022     getAutoCreate : function(){
9023         
9024         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9025         
9026         var id = Roo.id();
9027         
9028         var cfg = {};
9029         
9030         var input =  {
9031             tag: 'textarea',
9032             id : id,
9033             warp : this.warp,
9034             rows : this.rows,
9035             value : this.value || '',
9036             html: this.html || '',
9037             cls : 'form-control',
9038             placeholder : this.placeholder || '' 
9039             
9040         };
9041         
9042         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9043             input.maxLength = this.maxLength;
9044         }
9045         
9046         if(this.resize){
9047             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9048         }
9049         
9050         if(this.cols){
9051             input.cols = this.cols;
9052         }
9053         
9054         if (this.readOnly) {
9055             input.readonly = true;
9056         }
9057         
9058         if (this.name) {
9059             input.name = this.name;
9060         }
9061         
9062         if (this.size) {
9063             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9064         }
9065         
9066         var settings=this;
9067         ['xs','sm','md','lg'].map(function(size){
9068             if (settings[size]) {
9069                 cfg.cls += ' col-' + size + '-' + settings[size];
9070             }
9071         });
9072         
9073         var inputblock = input;
9074         
9075         if(this.hasFeedback && !this.allowBlank){
9076             
9077             var feedback = {
9078                 tag: 'span',
9079                 cls: 'glyphicon form-control-feedback'
9080             };
9081
9082             inputblock = {
9083                 cls : 'has-feedback',
9084                 cn :  [
9085                     input,
9086                     feedback
9087                 ] 
9088             };  
9089         }
9090         
9091         
9092         if (this.before || this.after) {
9093             
9094             inputblock = {
9095                 cls : 'input-group',
9096                 cn :  [] 
9097             };
9098             if (this.before) {
9099                 inputblock.cn.push({
9100                     tag :'span',
9101                     cls : 'input-group-addon',
9102                     html : this.before
9103                 });
9104             }
9105             
9106             inputblock.cn.push(input);
9107             
9108             if(this.hasFeedback && !this.allowBlank){
9109                 inputblock.cls += ' has-feedback';
9110                 inputblock.cn.push(feedback);
9111             }
9112             
9113             if (this.after) {
9114                 inputblock.cn.push({
9115                     tag :'span',
9116                     cls : 'input-group-addon',
9117                     html : this.after
9118                 });
9119             }
9120             
9121         }
9122         
9123         if (align ==='left' && this.fieldLabel.length) {
9124 //                Roo.log("left and has label");
9125                 cfg.cn = [
9126                     
9127                     {
9128                         tag: 'label',
9129                         'for' :  id,
9130                         cls : 'control-label col-sm-' + this.labelWidth,
9131                         html : this.fieldLabel
9132                         
9133                     },
9134                     {
9135                         cls : "col-sm-" + (12 - this.labelWidth), 
9136                         cn: [
9137                             inputblock
9138                         ]
9139                     }
9140                     
9141                 ];
9142         } else if ( this.fieldLabel.length) {
9143 //                Roo.log(" label");
9144                  cfg.cn = [
9145                    
9146                     {
9147                         tag: 'label',
9148                         //cls : 'input-group-addon',
9149                         html : this.fieldLabel
9150                         
9151                     },
9152                     
9153                     inputblock
9154                     
9155                 ];
9156
9157         } else {
9158             
9159 //                   Roo.log(" no label && no align");
9160                 cfg.cn = [
9161                     
9162                         inputblock
9163                     
9164                 ];
9165                 
9166                 
9167         }
9168         
9169         if (this.disabled) {
9170             input.disabled=true;
9171         }
9172         
9173         return cfg;
9174         
9175     },
9176     /**
9177      * return the real textarea element.
9178      */
9179     inputEl: function ()
9180     {
9181         return this.el.select('textarea.form-control',true).first();
9182     },
9183     
9184     /**
9185      * Clear any invalid styles/messages for this field
9186      */
9187     clearInvalid : function()
9188     {
9189         
9190         if(!this.el || this.preventMark){ // not rendered
9191             return;
9192         }
9193         
9194         var label = this.el.select('label', true).first();
9195         var icon = this.el.select('i.fa-star', true).first();
9196         
9197         if(label && icon){
9198             icon.remove();
9199         }
9200         
9201         this.el.removeClass(this.invalidClass);
9202         
9203         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9204             
9205             var feedback = this.el.select('.form-control-feedback', true).first();
9206             
9207             if(feedback){
9208                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9209             }
9210             
9211         }
9212         
9213         this.fireEvent('valid', this);
9214     },
9215     
9216      /**
9217      * Mark this field as valid
9218      */
9219     markValid : function()
9220     {
9221         if(!this.el  || this.preventMark){ // not rendered
9222             return;
9223         }
9224         
9225         this.el.removeClass([this.invalidClass, this.validClass]);
9226         
9227         var feedback = this.el.select('.form-control-feedback', true).first();
9228             
9229         if(feedback){
9230             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9231         }
9232
9233         if(this.disabled || this.allowBlank){
9234             return;
9235         }
9236         
9237         var label = this.el.select('label', true).first();
9238         var icon = this.el.select('i.fa-star', true).first();
9239         
9240         if(label && icon){
9241             icon.remove();
9242         }
9243         
9244         this.el.addClass(this.validClass);
9245         
9246         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9247             
9248             var feedback = this.el.select('.form-control-feedback', true).first();
9249             
9250             if(feedback){
9251                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9252                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9253             }
9254             
9255         }
9256         
9257         this.fireEvent('valid', this);
9258     },
9259     
9260      /**
9261      * Mark this field as invalid
9262      * @param {String} msg The validation message
9263      */
9264     markInvalid : function(msg)
9265     {
9266         if(!this.el  || this.preventMark){ // not rendered
9267             return;
9268         }
9269         
9270         this.el.removeClass([this.invalidClass, this.validClass]);
9271         
9272         var feedback = this.el.select('.form-control-feedback', true).first();
9273             
9274         if(feedback){
9275             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9276         }
9277
9278         if(this.disabled || this.allowBlank){
9279             return;
9280         }
9281         
9282         var label = this.el.select('label', true).first();
9283         var icon = this.el.select('i.fa-star', true).first();
9284         
9285         if(!this.getValue().length && label && !icon){
9286             this.el.createChild({
9287                 tag : 'i',
9288                 cls : 'text-danger fa fa-lg fa-star',
9289                 tooltip : 'This field is required',
9290                 style : 'margin-right:5px;'
9291             }, label, true);
9292         }
9293
9294         this.el.addClass(this.invalidClass);
9295         
9296         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9297             
9298             var feedback = this.el.select('.form-control-feedback', true).first();
9299             
9300             if(feedback){
9301                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9302                 
9303                 if(this.getValue().length || this.forceFeedback){
9304                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9305                 }
9306                 
9307             }
9308             
9309         }
9310         
9311         this.fireEvent('invalid', this, msg);
9312     }
9313 });
9314
9315  
9316 /*
9317  * - LGPL
9318  *
9319  * trigger field - base class for combo..
9320  * 
9321  */
9322  
9323 /**
9324  * @class Roo.bootstrap.TriggerField
9325  * @extends Roo.bootstrap.Input
9326  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9327  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9328  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9329  * for which you can provide a custom implementation.  For example:
9330  * <pre><code>
9331 var trigger = new Roo.bootstrap.TriggerField();
9332 trigger.onTriggerClick = myTriggerFn;
9333 trigger.applyTo('my-field');
9334 </code></pre>
9335  *
9336  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9337  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9338  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9339  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9340  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9341
9342  * @constructor
9343  * Create a new TriggerField.
9344  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9345  * to the base TextField)
9346  */
9347 Roo.bootstrap.TriggerField = function(config){
9348     this.mimicing = false;
9349     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9350 };
9351
9352 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9353     /**
9354      * @cfg {String} triggerClass A CSS class to apply to the trigger
9355      */
9356      /**
9357      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9358      */
9359     hideTrigger:false,
9360
9361     /**
9362      * @cfg {Boolean} removable (true|false) special filter default false
9363      */
9364     removable : false,
9365     
9366     /** @cfg {Boolean} grow @hide */
9367     /** @cfg {Number} growMin @hide */
9368     /** @cfg {Number} growMax @hide */
9369
9370     /**
9371      * @hide 
9372      * @method
9373      */
9374     autoSize: Roo.emptyFn,
9375     // private
9376     monitorTab : true,
9377     // private
9378     deferHeight : true,
9379
9380     
9381     actionMode : 'wrap',
9382     
9383     caret : false,
9384     
9385     
9386     getAutoCreate : function(){
9387        
9388         var align = this.labelAlign || this.parentLabelAlign();
9389         
9390         var id = Roo.id();
9391         
9392         var cfg = {
9393             cls: 'form-group' //input-group
9394         };
9395         
9396         
9397         var input =  {
9398             tag: 'input',
9399             id : id,
9400             type : this.inputType,
9401             cls : 'form-control',
9402             autocomplete: 'new-password',
9403             placeholder : this.placeholder || '' 
9404             
9405         };
9406         if (this.name) {
9407             input.name = this.name;
9408         }
9409         if (this.size) {
9410             input.cls += ' input-' + this.size;
9411         }
9412         
9413         if (this.disabled) {
9414             input.disabled=true;
9415         }
9416         
9417         var inputblock = input;
9418         
9419         if(this.hasFeedback && !this.allowBlank){
9420             
9421             var feedback = {
9422                 tag: 'span',
9423                 cls: 'glyphicon form-control-feedback'
9424             };
9425             
9426             if(this.removable && !this.editable && !this.tickable){
9427                 inputblock = {
9428                     cls : 'has-feedback',
9429                     cn :  [
9430                         inputblock,
9431                         {
9432                             tag: 'button',
9433                             html : 'x',
9434                             cls : 'roo-combo-removable-btn close'
9435                         },
9436                         feedback
9437                     ] 
9438                 };
9439             } else {
9440                 inputblock = {
9441                     cls : 'has-feedback',
9442                     cn :  [
9443                         inputblock,
9444                         feedback
9445                     ] 
9446                 };
9447             }
9448
9449         } else {
9450             if(this.removable && !this.editable && !this.tickable){
9451                 inputblock = {
9452                     cls : 'roo-removable',
9453                     cn :  [
9454                         inputblock,
9455                         {
9456                             tag: 'button',
9457                             html : 'x',
9458                             cls : 'roo-combo-removable-btn close'
9459                         }
9460                     ] 
9461                 };
9462             }
9463         }
9464         
9465         if (this.before || this.after) {
9466             
9467             inputblock = {
9468                 cls : 'input-group',
9469                 cn :  [] 
9470             };
9471             if (this.before) {
9472                 inputblock.cn.push({
9473                     tag :'span',
9474                     cls : 'input-group-addon',
9475                     html : this.before
9476                 });
9477             }
9478             
9479             inputblock.cn.push(input);
9480             
9481             if(this.hasFeedback && !this.allowBlank){
9482                 inputblock.cls += ' has-feedback';
9483                 inputblock.cn.push(feedback);
9484             }
9485             
9486             if (this.after) {
9487                 inputblock.cn.push({
9488                     tag :'span',
9489                     cls : 'input-group-addon',
9490                     html : this.after
9491                 });
9492             }
9493             
9494         };
9495         
9496         var box = {
9497             tag: 'div',
9498             cn: [
9499                 {
9500                     tag: 'input',
9501                     type : 'hidden',
9502                     cls: 'form-hidden-field'
9503                 },
9504                 inputblock
9505             ]
9506             
9507         };
9508         
9509         if(this.multiple){
9510             box = {
9511                 tag: 'div',
9512                 cn: [
9513                     {
9514                         tag: 'input',
9515                         type : 'hidden',
9516                         cls: 'form-hidden-field'
9517                     },
9518                     {
9519                         tag: 'ul',
9520                         cls: 'roo-select2-choices',
9521                         cn:[
9522                             {
9523                                 tag: 'li',
9524                                 cls: 'roo-select2-search-field',
9525                                 cn: [
9526
9527                                     inputblock
9528                                 ]
9529                             }
9530                         ]
9531                     }
9532                 ]
9533             }
9534         };
9535         
9536         var combobox = {
9537             cls: 'roo-select2-container input-group',
9538             cn: [
9539                 box
9540 //                {
9541 //                    tag: 'ul',
9542 //                    cls: 'typeahead typeahead-long dropdown-menu',
9543 //                    style: 'display:none'
9544 //                }
9545             ]
9546         };
9547         
9548         if(!this.multiple && this.showToggleBtn){
9549             
9550             var caret = {
9551                         tag: 'span',
9552                         cls: 'caret'
9553              };
9554             if (this.caret != false) {
9555                 caret = {
9556                      tag: 'i',
9557                      cls: 'fa fa-' + this.caret
9558                 };
9559                 
9560             }
9561             
9562             combobox.cn.push({
9563                 tag :'span',
9564                 cls : 'input-group-addon btn dropdown-toggle',
9565                 cn : [
9566                     caret,
9567                     {
9568                         tag: 'span',
9569                         cls: 'combobox-clear',
9570                         cn  : [
9571                             {
9572                                 tag : 'i',
9573                                 cls: 'icon-remove'
9574                             }
9575                         ]
9576                     }
9577                 ]
9578
9579             })
9580         }
9581         
9582         if(this.multiple){
9583             combobox.cls += ' roo-select2-container-multi';
9584         }
9585         
9586         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9587             
9588 //                Roo.log("left and has label");
9589             cfg.cn = [
9590                 {
9591                     tag : 'i',
9592                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9593                     tooltip : 'This field is required'
9594                 },
9595                 {
9596                     tag: 'label',
9597                     'for' :  id,
9598                     cls : 'control-label col-sm-' + this.labelWidth,
9599                     html : this.fieldLabel
9600
9601                 },
9602                 {
9603                     cls : "col-sm-" + (12 - this.labelWidth), 
9604                     cn: [
9605                         combobox
9606                     ]
9607                 }
9608
9609             ];
9610             
9611             if(this.indicatorpos == 'right'){
9612                 cfg.cn = [
9613                     {
9614                         tag: 'label',
9615                         'for' :  id,
9616                         cls : 'control-label col-sm-' + this.labelWidth,
9617                         html : this.fieldLabel
9618
9619                     },
9620                     {
9621                         tag : 'i',
9622                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9623                         tooltip : 'This field is required'
9624                     },
9625                     {
9626                         cls : "col-sm-" + (12 - this.labelWidth), 
9627                         cn: [
9628                             combobox
9629                         ]
9630                     }
9631
9632                 ];
9633             }
9634             
9635         } else if ( this.fieldLabel.length) {
9636 //                Roo.log(" label");
9637             cfg.cn = [
9638                 {
9639                    tag : 'i',
9640                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9641                    tooltip : 'This field is required'
9642                },
9643                {
9644                    tag: 'label',
9645                    //cls : 'input-group-addon',
9646                    html : this.fieldLabel
9647
9648                },
9649
9650                combobox
9651
9652             ];
9653             
9654             if(this.indicatorpos == 'right'){
9655                 
9656                 cfg.cn = [
9657                     {
9658                        tag: 'label',
9659                        //cls : 'input-group-addon',
9660                        html : this.fieldLabel
9661
9662                     },
9663                     {
9664                        tag : 'i',
9665                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9666                        tooltip : 'This field is required'
9667                     },
9668                     
9669                     combobox
9670
9671                 ];
9672
9673             }
9674
9675         } else {
9676             
9677 //                Roo.log(" no label && no align");
9678                 cfg = combobox
9679                      
9680                 
9681         }
9682          
9683         var settings=this;
9684         ['xs','sm','md','lg'].map(function(size){
9685             if (settings[size]) {
9686                 cfg.cls += ' col-' + size + '-' + settings[size];
9687             }
9688         });
9689         
9690         return cfg;
9691         
9692     },
9693     
9694     
9695     
9696     // private
9697     onResize : function(w, h){
9698 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9699 //        if(typeof w == 'number'){
9700 //            var x = w - this.trigger.getWidth();
9701 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9702 //            this.trigger.setStyle('left', x+'px');
9703 //        }
9704     },
9705
9706     // private
9707     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9708
9709     // private
9710     getResizeEl : function(){
9711         return this.inputEl();
9712     },
9713
9714     // private
9715     getPositionEl : function(){
9716         return this.inputEl();
9717     },
9718
9719     // private
9720     alignErrorIcon : function(){
9721         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9722     },
9723
9724     // private
9725     initEvents : function(){
9726         
9727         this.createList();
9728         
9729         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9730         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9731         if(!this.multiple && this.showToggleBtn){
9732             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9733             if(this.hideTrigger){
9734                 this.trigger.setDisplayed(false);
9735             }
9736             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9737         }
9738         
9739         if(this.multiple){
9740             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9741         }
9742         
9743         if(this.removable && !this.editable && !this.tickable){
9744             var close = this.closeTriggerEl();
9745             
9746             if(close){
9747                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9748                 close.on('click', this.removeBtnClick, this, close);
9749             }
9750         }
9751         
9752         //this.trigger.addClassOnOver('x-form-trigger-over');
9753         //this.trigger.addClassOnClick('x-form-trigger-click');
9754         
9755         //if(!this.width){
9756         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9757         //}
9758     },
9759     
9760     closeTriggerEl : function()
9761     {
9762         var close = this.el.select('.roo-combo-removable-btn', true).first();
9763         return close ? close : false;
9764     },
9765     
9766     removeBtnClick : function(e, h, el)
9767     {
9768         e.preventDefault();
9769         
9770         if(this.fireEvent("remove", this) !== false){
9771             this.reset();
9772             this.fireEvent("afterremove", this)
9773         }
9774     },
9775     
9776     createList : function()
9777     {
9778         this.list = Roo.get(document.body).createChild({
9779             tag: 'ul',
9780             cls: 'typeahead typeahead-long dropdown-menu',
9781             style: 'display:none'
9782         });
9783         
9784         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9785         
9786     },
9787
9788     // private
9789     initTrigger : function(){
9790        
9791     },
9792
9793     // private
9794     onDestroy : function(){
9795         if(this.trigger){
9796             this.trigger.removeAllListeners();
9797           //  this.trigger.remove();
9798         }
9799         //if(this.wrap){
9800         //    this.wrap.remove();
9801         //}
9802         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9803     },
9804
9805     // private
9806     onFocus : function(){
9807         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9808         /*
9809         if(!this.mimicing){
9810             this.wrap.addClass('x-trigger-wrap-focus');
9811             this.mimicing = true;
9812             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9813             if(this.monitorTab){
9814                 this.el.on("keydown", this.checkTab, this);
9815             }
9816         }
9817         */
9818     },
9819
9820     // private
9821     checkTab : function(e){
9822         if(e.getKey() == e.TAB){
9823             this.triggerBlur();
9824         }
9825     },
9826
9827     // private
9828     onBlur : function(){
9829         // do nothing
9830     },
9831
9832     // private
9833     mimicBlur : function(e, t){
9834         /*
9835         if(!this.wrap.contains(t) && this.validateBlur()){
9836             this.triggerBlur();
9837         }
9838         */
9839     },
9840
9841     // private
9842     triggerBlur : function(){
9843         this.mimicing = false;
9844         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9845         if(this.monitorTab){
9846             this.el.un("keydown", this.checkTab, this);
9847         }
9848         //this.wrap.removeClass('x-trigger-wrap-focus');
9849         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9850     },
9851
9852     // private
9853     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9854     validateBlur : function(e, t){
9855         return true;
9856     },
9857
9858     // private
9859     onDisable : function(){
9860         this.inputEl().dom.disabled = true;
9861         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9862         //if(this.wrap){
9863         //    this.wrap.addClass('x-item-disabled');
9864         //}
9865     },
9866
9867     // private
9868     onEnable : function(){
9869         this.inputEl().dom.disabled = false;
9870         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9871         //if(this.wrap){
9872         //    this.el.removeClass('x-item-disabled');
9873         //}
9874     },
9875
9876     // private
9877     onShow : function(){
9878         var ae = this.getActionEl();
9879         
9880         if(ae){
9881             ae.dom.style.display = '';
9882             ae.dom.style.visibility = 'visible';
9883         }
9884     },
9885
9886     // private
9887     
9888     onHide : function(){
9889         var ae = this.getActionEl();
9890         ae.dom.style.display = 'none';
9891     },
9892
9893     /**
9894      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9895      * by an implementing function.
9896      * @method
9897      * @param {EventObject} e
9898      */
9899     onTriggerClick : Roo.emptyFn
9900 });
9901  /*
9902  * Based on:
9903  * Ext JS Library 1.1.1
9904  * Copyright(c) 2006-2007, Ext JS, LLC.
9905  *
9906  * Originally Released Under LGPL - original licence link has changed is not relivant.
9907  *
9908  * Fork - LGPL
9909  * <script type="text/javascript">
9910  */
9911
9912
9913 /**
9914  * @class Roo.data.SortTypes
9915  * @singleton
9916  * Defines the default sorting (casting?) comparison functions used when sorting data.
9917  */
9918 Roo.data.SortTypes = {
9919     /**
9920      * Default sort that does nothing
9921      * @param {Mixed} s The value being converted
9922      * @return {Mixed} The comparison value
9923      */
9924     none : function(s){
9925         return s;
9926     },
9927     
9928     /**
9929      * The regular expression used to strip tags
9930      * @type {RegExp}
9931      * @property
9932      */
9933     stripTagsRE : /<\/?[^>]+>/gi,
9934     
9935     /**
9936      * Strips all HTML tags to sort on text only
9937      * @param {Mixed} s The value being converted
9938      * @return {String} The comparison value
9939      */
9940     asText : function(s){
9941         return String(s).replace(this.stripTagsRE, "");
9942     },
9943     
9944     /**
9945      * Strips all HTML tags to sort on text only - Case insensitive
9946      * @param {Mixed} s The value being converted
9947      * @return {String} The comparison value
9948      */
9949     asUCText : function(s){
9950         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9951     },
9952     
9953     /**
9954      * Case insensitive string
9955      * @param {Mixed} s The value being converted
9956      * @return {String} The comparison value
9957      */
9958     asUCString : function(s) {
9959         return String(s).toUpperCase();
9960     },
9961     
9962     /**
9963      * Date sorting
9964      * @param {Mixed} s The value being converted
9965      * @return {Number} The comparison value
9966      */
9967     asDate : function(s) {
9968         if(!s){
9969             return 0;
9970         }
9971         if(s instanceof Date){
9972             return s.getTime();
9973         }
9974         return Date.parse(String(s));
9975     },
9976     
9977     /**
9978      * Float sorting
9979      * @param {Mixed} s The value being converted
9980      * @return {Float} The comparison value
9981      */
9982     asFloat : function(s) {
9983         var val = parseFloat(String(s).replace(/,/g, ""));
9984         if(isNaN(val)) {
9985             val = 0;
9986         }
9987         return val;
9988     },
9989     
9990     /**
9991      * Integer sorting
9992      * @param {Mixed} s The value being converted
9993      * @return {Number} The comparison value
9994      */
9995     asInt : function(s) {
9996         var val = parseInt(String(s).replace(/,/g, ""));
9997         if(isNaN(val)) {
9998             val = 0;
9999         }
10000         return val;
10001     }
10002 };/*
10003  * Based on:
10004  * Ext JS Library 1.1.1
10005  * Copyright(c) 2006-2007, Ext JS, LLC.
10006  *
10007  * Originally Released Under LGPL - original licence link has changed is not relivant.
10008  *
10009  * Fork - LGPL
10010  * <script type="text/javascript">
10011  */
10012
10013 /**
10014 * @class Roo.data.Record
10015  * Instances of this class encapsulate both record <em>definition</em> information, and record
10016  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10017  * to access Records cached in an {@link Roo.data.Store} object.<br>
10018  * <p>
10019  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10020  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10021  * objects.<br>
10022  * <p>
10023  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10024  * @constructor
10025  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10026  * {@link #create}. The parameters are the same.
10027  * @param {Array} data An associative Array of data values keyed by the field name.
10028  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10029  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10030  * not specified an integer id is generated.
10031  */
10032 Roo.data.Record = function(data, id){
10033     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10034     this.data = data;
10035 };
10036
10037 /**
10038  * Generate a constructor for a specific record layout.
10039  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10040  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10041  * Each field definition object may contain the following properties: <ul>
10042  * <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,
10043  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10044  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10045  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10046  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10047  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10048  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10049  * this may be omitted.</p></li>
10050  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10051  * <ul><li>auto (Default, implies no conversion)</li>
10052  * <li>string</li>
10053  * <li>int</li>
10054  * <li>float</li>
10055  * <li>boolean</li>
10056  * <li>date</li></ul></p></li>
10057  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10058  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10059  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10060  * by the Reader into an object that will be stored in the Record. It is passed the
10061  * following parameters:<ul>
10062  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10063  * </ul></p></li>
10064  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10065  * </ul>
10066  * <br>usage:<br><pre><code>
10067 var TopicRecord = Roo.data.Record.create(
10068     {name: 'title', mapping: 'topic_title'},
10069     {name: 'author', mapping: 'username'},
10070     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10071     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10072     {name: 'lastPoster', mapping: 'user2'},
10073     {name: 'excerpt', mapping: 'post_text'}
10074 );
10075
10076 var myNewRecord = new TopicRecord({
10077     title: 'Do my job please',
10078     author: 'noobie',
10079     totalPosts: 1,
10080     lastPost: new Date(),
10081     lastPoster: 'Animal',
10082     excerpt: 'No way dude!'
10083 });
10084 myStore.add(myNewRecord);
10085 </code></pre>
10086  * @method create
10087  * @static
10088  */
10089 Roo.data.Record.create = function(o){
10090     var f = function(){
10091         f.superclass.constructor.apply(this, arguments);
10092     };
10093     Roo.extend(f, Roo.data.Record);
10094     var p = f.prototype;
10095     p.fields = new Roo.util.MixedCollection(false, function(field){
10096         return field.name;
10097     });
10098     for(var i = 0, len = o.length; i < len; i++){
10099         p.fields.add(new Roo.data.Field(o[i]));
10100     }
10101     f.getField = function(name){
10102         return p.fields.get(name);  
10103     };
10104     return f;
10105 };
10106
10107 Roo.data.Record.AUTO_ID = 1000;
10108 Roo.data.Record.EDIT = 'edit';
10109 Roo.data.Record.REJECT = 'reject';
10110 Roo.data.Record.COMMIT = 'commit';
10111
10112 Roo.data.Record.prototype = {
10113     /**
10114      * Readonly flag - true if this record has been modified.
10115      * @type Boolean
10116      */
10117     dirty : false,
10118     editing : false,
10119     error: null,
10120     modified: null,
10121
10122     // private
10123     join : function(store){
10124         this.store = store;
10125     },
10126
10127     /**
10128      * Set the named field to the specified value.
10129      * @param {String} name The name of the field to set.
10130      * @param {Object} value The value to set the field to.
10131      */
10132     set : function(name, value){
10133         if(this.data[name] == value){
10134             return;
10135         }
10136         this.dirty = true;
10137         if(!this.modified){
10138             this.modified = {};
10139         }
10140         if(typeof this.modified[name] == 'undefined'){
10141             this.modified[name] = this.data[name];
10142         }
10143         this.data[name] = value;
10144         if(!this.editing && this.store){
10145             this.store.afterEdit(this);
10146         }       
10147     },
10148
10149     /**
10150      * Get the value of the named field.
10151      * @param {String} name The name of the field to get the value of.
10152      * @return {Object} The value of the field.
10153      */
10154     get : function(name){
10155         return this.data[name]; 
10156     },
10157
10158     // private
10159     beginEdit : function(){
10160         this.editing = true;
10161         this.modified = {}; 
10162     },
10163
10164     // private
10165     cancelEdit : function(){
10166         this.editing = false;
10167         delete this.modified;
10168     },
10169
10170     // private
10171     endEdit : function(){
10172         this.editing = false;
10173         if(this.dirty && this.store){
10174             this.store.afterEdit(this);
10175         }
10176     },
10177
10178     /**
10179      * Usually called by the {@link Roo.data.Store} which owns the Record.
10180      * Rejects all changes made to the Record since either creation, or the last commit operation.
10181      * Modified fields are reverted to their original values.
10182      * <p>
10183      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10184      * of reject operations.
10185      */
10186     reject : function(){
10187         var m = this.modified;
10188         for(var n in m){
10189             if(typeof m[n] != "function"){
10190                 this.data[n] = m[n];
10191             }
10192         }
10193         this.dirty = false;
10194         delete this.modified;
10195         this.editing = false;
10196         if(this.store){
10197             this.store.afterReject(this);
10198         }
10199     },
10200
10201     /**
10202      * Usually called by the {@link Roo.data.Store} which owns the Record.
10203      * Commits all changes made to the Record since either creation, or the last commit operation.
10204      * <p>
10205      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10206      * of commit operations.
10207      */
10208     commit : function(){
10209         this.dirty = false;
10210         delete this.modified;
10211         this.editing = false;
10212         if(this.store){
10213             this.store.afterCommit(this);
10214         }
10215     },
10216
10217     // private
10218     hasError : function(){
10219         return this.error != null;
10220     },
10221
10222     // private
10223     clearError : function(){
10224         this.error = null;
10225     },
10226
10227     /**
10228      * Creates a copy of this record.
10229      * @param {String} id (optional) A new record id if you don't want to use this record's id
10230      * @return {Record}
10231      */
10232     copy : function(newId) {
10233         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10234     }
10235 };/*
10236  * Based on:
10237  * Ext JS Library 1.1.1
10238  * Copyright(c) 2006-2007, Ext JS, LLC.
10239  *
10240  * Originally Released Under LGPL - original licence link has changed is not relivant.
10241  *
10242  * Fork - LGPL
10243  * <script type="text/javascript">
10244  */
10245
10246
10247
10248 /**
10249  * @class Roo.data.Store
10250  * @extends Roo.util.Observable
10251  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10252  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10253  * <p>
10254  * 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
10255  * has no knowledge of the format of the data returned by the Proxy.<br>
10256  * <p>
10257  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10258  * instances from the data object. These records are cached and made available through accessor functions.
10259  * @constructor
10260  * Creates a new Store.
10261  * @param {Object} config A config object containing the objects needed for the Store to access data,
10262  * and read the data into Records.
10263  */
10264 Roo.data.Store = function(config){
10265     this.data = new Roo.util.MixedCollection(false);
10266     this.data.getKey = function(o){
10267         return o.id;
10268     };
10269     this.baseParams = {};
10270     // private
10271     this.paramNames = {
10272         "start" : "start",
10273         "limit" : "limit",
10274         "sort" : "sort",
10275         "dir" : "dir",
10276         "multisort" : "_multisort"
10277     };
10278
10279     if(config && config.data){
10280         this.inlineData = config.data;
10281         delete config.data;
10282     }
10283
10284     Roo.apply(this, config);
10285     
10286     if(this.reader){ // reader passed
10287         this.reader = Roo.factory(this.reader, Roo.data);
10288         this.reader.xmodule = this.xmodule || false;
10289         if(!this.recordType){
10290             this.recordType = this.reader.recordType;
10291         }
10292         if(this.reader.onMetaChange){
10293             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10294         }
10295     }
10296
10297     if(this.recordType){
10298         this.fields = this.recordType.prototype.fields;
10299     }
10300     this.modified = [];
10301
10302     this.addEvents({
10303         /**
10304          * @event datachanged
10305          * Fires when the data cache has changed, and a widget which is using this Store
10306          * as a Record cache should refresh its view.
10307          * @param {Store} this
10308          */
10309         datachanged : true,
10310         /**
10311          * @event metachange
10312          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10313          * @param {Store} this
10314          * @param {Object} meta The JSON metadata
10315          */
10316         metachange : true,
10317         /**
10318          * @event add
10319          * Fires when Records have been added to the Store
10320          * @param {Store} this
10321          * @param {Roo.data.Record[]} records The array of Records added
10322          * @param {Number} index The index at which the record(s) were added
10323          */
10324         add : true,
10325         /**
10326          * @event remove
10327          * Fires when a Record has been removed from the Store
10328          * @param {Store} this
10329          * @param {Roo.data.Record} record The Record that was removed
10330          * @param {Number} index The index at which the record was removed
10331          */
10332         remove : true,
10333         /**
10334          * @event update
10335          * Fires when a Record has been updated
10336          * @param {Store} this
10337          * @param {Roo.data.Record} record The Record that was updated
10338          * @param {String} operation The update operation being performed.  Value may be one of:
10339          * <pre><code>
10340  Roo.data.Record.EDIT
10341  Roo.data.Record.REJECT
10342  Roo.data.Record.COMMIT
10343          * </code></pre>
10344          */
10345         update : true,
10346         /**
10347          * @event clear
10348          * Fires when the data cache has been cleared.
10349          * @param {Store} this
10350          */
10351         clear : true,
10352         /**
10353          * @event beforeload
10354          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10355          * the load action will be canceled.
10356          * @param {Store} this
10357          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10358          */
10359         beforeload : true,
10360         /**
10361          * @event beforeloadadd
10362          * Fires after a new set of Records has been loaded.
10363          * @param {Store} this
10364          * @param {Roo.data.Record[]} records The Records that were loaded
10365          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10366          */
10367         beforeloadadd : true,
10368         /**
10369          * @event load
10370          * Fires after a new set of Records has been loaded, before they are added to the store.
10371          * @param {Store} this
10372          * @param {Roo.data.Record[]} records The Records that were loaded
10373          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10374          * @params {Object} return from reader
10375          */
10376         load : true,
10377         /**
10378          * @event loadexception
10379          * Fires if an exception occurs in the Proxy during loading.
10380          * Called with the signature of the Proxy's "loadexception" event.
10381          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10382          * 
10383          * @param {Proxy} 
10384          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10385          * @param {Object} load options 
10386          * @param {Object} jsonData from your request (normally this contains the Exception)
10387          */
10388         loadexception : true
10389     });
10390     
10391     if(this.proxy){
10392         this.proxy = Roo.factory(this.proxy, Roo.data);
10393         this.proxy.xmodule = this.xmodule || false;
10394         this.relayEvents(this.proxy,  ["loadexception"]);
10395     }
10396     this.sortToggle = {};
10397     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10398
10399     Roo.data.Store.superclass.constructor.call(this);
10400
10401     if(this.inlineData){
10402         this.loadData(this.inlineData);
10403         delete this.inlineData;
10404     }
10405 };
10406
10407 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10408      /**
10409     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10410     * without a remote query - used by combo/forms at present.
10411     */
10412     
10413     /**
10414     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10415     */
10416     /**
10417     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10418     */
10419     /**
10420     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10421     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10422     */
10423     /**
10424     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10425     * on any HTTP request
10426     */
10427     /**
10428     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10429     */
10430     /**
10431     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10432     */
10433     multiSort: false,
10434     /**
10435     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10436     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10437     */
10438     remoteSort : false,
10439
10440     /**
10441     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10442      * loaded or when a record is removed. (defaults to false).
10443     */
10444     pruneModifiedRecords : false,
10445
10446     // private
10447     lastOptions : null,
10448
10449     /**
10450      * Add Records to the Store and fires the add event.
10451      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10452      */
10453     add : function(records){
10454         records = [].concat(records);
10455         for(var i = 0, len = records.length; i < len; i++){
10456             records[i].join(this);
10457         }
10458         var index = this.data.length;
10459         this.data.addAll(records);
10460         this.fireEvent("add", this, records, index);
10461     },
10462
10463     /**
10464      * Remove a Record from the Store and fires the remove event.
10465      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10466      */
10467     remove : function(record){
10468         var index = this.data.indexOf(record);
10469         this.data.removeAt(index);
10470         if(this.pruneModifiedRecords){
10471             this.modified.remove(record);
10472         }
10473         this.fireEvent("remove", this, record, index);
10474     },
10475
10476     /**
10477      * Remove all Records from the Store and fires the clear event.
10478      */
10479     removeAll : function(){
10480         this.data.clear();
10481         if(this.pruneModifiedRecords){
10482             this.modified = [];
10483         }
10484         this.fireEvent("clear", this);
10485     },
10486
10487     /**
10488      * Inserts Records to the Store at the given index and fires the add event.
10489      * @param {Number} index The start index at which to insert the passed Records.
10490      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10491      */
10492     insert : function(index, records){
10493         records = [].concat(records);
10494         for(var i = 0, len = records.length; i < len; i++){
10495             this.data.insert(index, records[i]);
10496             records[i].join(this);
10497         }
10498         this.fireEvent("add", this, records, index);
10499     },
10500
10501     /**
10502      * Get the index within the cache of the passed Record.
10503      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10504      * @return {Number} The index of the passed Record. Returns -1 if not found.
10505      */
10506     indexOf : function(record){
10507         return this.data.indexOf(record);
10508     },
10509
10510     /**
10511      * Get the index within the cache of the Record with the passed id.
10512      * @param {String} id The id of the Record to find.
10513      * @return {Number} The index of the Record. Returns -1 if not found.
10514      */
10515     indexOfId : function(id){
10516         return this.data.indexOfKey(id);
10517     },
10518
10519     /**
10520      * Get the Record with the specified id.
10521      * @param {String} id The id of the Record to find.
10522      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10523      */
10524     getById : function(id){
10525         return this.data.key(id);
10526     },
10527
10528     /**
10529      * Get the Record at the specified index.
10530      * @param {Number} index The index of the Record to find.
10531      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10532      */
10533     getAt : function(index){
10534         return this.data.itemAt(index);
10535     },
10536
10537     /**
10538      * Returns a range of Records between specified indices.
10539      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10540      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10541      * @return {Roo.data.Record[]} An array of Records
10542      */
10543     getRange : function(start, end){
10544         return this.data.getRange(start, end);
10545     },
10546
10547     // private
10548     storeOptions : function(o){
10549         o = Roo.apply({}, o);
10550         delete o.callback;
10551         delete o.scope;
10552         this.lastOptions = o;
10553     },
10554
10555     /**
10556      * Loads the Record cache from the configured Proxy using the configured Reader.
10557      * <p>
10558      * If using remote paging, then the first load call must specify the <em>start</em>
10559      * and <em>limit</em> properties in the options.params property to establish the initial
10560      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10561      * <p>
10562      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10563      * and this call will return before the new data has been loaded. Perform any post-processing
10564      * in a callback function, or in a "load" event handler.</strong>
10565      * <p>
10566      * @param {Object} options An object containing properties which control loading options:<ul>
10567      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10568      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10569      * passed the following arguments:<ul>
10570      * <li>r : Roo.data.Record[]</li>
10571      * <li>options: Options object from the load call</li>
10572      * <li>success: Boolean success indicator</li></ul></li>
10573      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10574      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10575      * </ul>
10576      */
10577     load : function(options){
10578         options = options || {};
10579         if(this.fireEvent("beforeload", this, options) !== false){
10580             this.storeOptions(options);
10581             var p = Roo.apply(options.params || {}, this.baseParams);
10582             // if meta was not loaded from remote source.. try requesting it.
10583             if (!this.reader.metaFromRemote) {
10584                 p._requestMeta = 1;
10585             }
10586             if(this.sortInfo && this.remoteSort){
10587                 var pn = this.paramNames;
10588                 p[pn["sort"]] = this.sortInfo.field;
10589                 p[pn["dir"]] = this.sortInfo.direction;
10590             }
10591             if (this.multiSort) {
10592                 var pn = this.paramNames;
10593                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10594             }
10595             
10596             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10597         }
10598     },
10599
10600     /**
10601      * Reloads the Record cache from the configured Proxy using the configured Reader and
10602      * the options from the last load operation performed.
10603      * @param {Object} options (optional) An object containing properties which may override the options
10604      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10605      * the most recently used options are reused).
10606      */
10607     reload : function(options){
10608         this.load(Roo.applyIf(options||{}, this.lastOptions));
10609     },
10610
10611     // private
10612     // Called as a callback by the Reader during a load operation.
10613     loadRecords : function(o, options, success){
10614         if(!o || success === false){
10615             if(success !== false){
10616                 this.fireEvent("load", this, [], options, o);
10617             }
10618             if(options.callback){
10619                 options.callback.call(options.scope || this, [], options, false);
10620             }
10621             return;
10622         }
10623         // if data returned failure - throw an exception.
10624         if (o.success === false) {
10625             // show a message if no listener is registered.
10626             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10627                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10628             }
10629             // loadmask wil be hooked into this..
10630             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10631             return;
10632         }
10633         var r = o.records, t = o.totalRecords || r.length;
10634         
10635         this.fireEvent("beforeloadadd", this, r, options, o);
10636         
10637         if(!options || options.add !== true){
10638             if(this.pruneModifiedRecords){
10639                 this.modified = [];
10640             }
10641             for(var i = 0, len = r.length; i < len; i++){
10642                 r[i].join(this);
10643             }
10644             if(this.snapshot){
10645                 this.data = this.snapshot;
10646                 delete this.snapshot;
10647             }
10648             this.data.clear();
10649             this.data.addAll(r);
10650             this.totalLength = t;
10651             this.applySort();
10652             this.fireEvent("datachanged", this);
10653         }else{
10654             this.totalLength = Math.max(t, this.data.length+r.length);
10655             this.add(r);
10656         }
10657         this.fireEvent("load", this, r, options, o);
10658         if(options.callback){
10659             options.callback.call(options.scope || this, r, options, true);
10660         }
10661     },
10662
10663
10664     /**
10665      * Loads data from a passed data block. A Reader which understands the format of the data
10666      * must have been configured in the constructor.
10667      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10668      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10669      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10670      */
10671     loadData : function(o, append){
10672         var r = this.reader.readRecords(o);
10673         this.loadRecords(r, {add: append}, true);
10674     },
10675
10676     /**
10677      * Gets the number of cached records.
10678      * <p>
10679      * <em>If using paging, this may not be the total size of the dataset. If the data object
10680      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10681      * the data set size</em>
10682      */
10683     getCount : function(){
10684         return this.data.length || 0;
10685     },
10686
10687     /**
10688      * Gets the total number of records in the dataset as returned by the server.
10689      * <p>
10690      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10691      * the dataset size</em>
10692      */
10693     getTotalCount : function(){
10694         return this.totalLength || 0;
10695     },
10696
10697     /**
10698      * Returns the sort state of the Store as an object with two properties:
10699      * <pre><code>
10700  field {String} The name of the field by which the Records are sorted
10701  direction {String} The sort order, "ASC" or "DESC"
10702      * </code></pre>
10703      */
10704     getSortState : function(){
10705         return this.sortInfo;
10706     },
10707
10708     // private
10709     applySort : function(){
10710         if(this.sortInfo && !this.remoteSort){
10711             var s = this.sortInfo, f = s.field;
10712             var st = this.fields.get(f).sortType;
10713             var fn = function(r1, r2){
10714                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10715                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10716             };
10717             this.data.sort(s.direction, fn);
10718             if(this.snapshot && this.snapshot != this.data){
10719                 this.snapshot.sort(s.direction, fn);
10720             }
10721         }
10722     },
10723
10724     /**
10725      * Sets the default sort column and order to be used by the next load operation.
10726      * @param {String} fieldName The name of the field to sort by.
10727      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10728      */
10729     setDefaultSort : function(field, dir){
10730         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10731     },
10732
10733     /**
10734      * Sort the Records.
10735      * If remote sorting is used, the sort is performed on the server, and the cache is
10736      * reloaded. If local sorting is used, the cache is sorted internally.
10737      * @param {String} fieldName The name of the field to sort by.
10738      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10739      */
10740     sort : function(fieldName, dir){
10741         var f = this.fields.get(fieldName);
10742         if(!dir){
10743             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10744             
10745             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10746                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10747             }else{
10748                 dir = f.sortDir;
10749             }
10750         }
10751         this.sortToggle[f.name] = dir;
10752         this.sortInfo = {field: f.name, direction: dir};
10753         if(!this.remoteSort){
10754             this.applySort();
10755             this.fireEvent("datachanged", this);
10756         }else{
10757             this.load(this.lastOptions);
10758         }
10759     },
10760
10761     /**
10762      * Calls the specified function for each of the Records in the cache.
10763      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10764      * Returning <em>false</em> aborts and exits the iteration.
10765      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10766      */
10767     each : function(fn, scope){
10768         this.data.each(fn, scope);
10769     },
10770
10771     /**
10772      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10773      * (e.g., during paging).
10774      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10775      */
10776     getModifiedRecords : function(){
10777         return this.modified;
10778     },
10779
10780     // private
10781     createFilterFn : function(property, value, anyMatch){
10782         if(!value.exec){ // not a regex
10783             value = String(value);
10784             if(value.length == 0){
10785                 return false;
10786             }
10787             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10788         }
10789         return function(r){
10790             return value.test(r.data[property]);
10791         };
10792     },
10793
10794     /**
10795      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10796      * @param {String} property A field on your records
10797      * @param {Number} start The record index to start at (defaults to 0)
10798      * @param {Number} end The last record index to include (defaults to length - 1)
10799      * @return {Number} The sum
10800      */
10801     sum : function(property, start, end){
10802         var rs = this.data.items, v = 0;
10803         start = start || 0;
10804         end = (end || end === 0) ? end : rs.length-1;
10805
10806         for(var i = start; i <= end; i++){
10807             v += (rs[i].data[property] || 0);
10808         }
10809         return v;
10810     },
10811
10812     /**
10813      * Filter the records by a specified property.
10814      * @param {String} field A field on your records
10815      * @param {String/RegExp} value Either a string that the field
10816      * should start with or a RegExp to test against the field
10817      * @param {Boolean} anyMatch True to match any part not just the beginning
10818      */
10819     filter : function(property, value, anyMatch){
10820         var fn = this.createFilterFn(property, value, anyMatch);
10821         return fn ? this.filterBy(fn) : this.clearFilter();
10822     },
10823
10824     /**
10825      * Filter by a function. The specified function will be called with each
10826      * record in this data source. If the function returns true the record is included,
10827      * otherwise it is filtered.
10828      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10829      * @param {Object} scope (optional) The scope of the function (defaults to this)
10830      */
10831     filterBy : function(fn, scope){
10832         this.snapshot = this.snapshot || this.data;
10833         this.data = this.queryBy(fn, scope||this);
10834         this.fireEvent("datachanged", this);
10835     },
10836
10837     /**
10838      * Query the records by a specified property.
10839      * @param {String} field A field on your records
10840      * @param {String/RegExp} value Either a string that the field
10841      * should start with or a RegExp to test against the field
10842      * @param {Boolean} anyMatch True to match any part not just the beginning
10843      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10844      */
10845     query : function(property, value, anyMatch){
10846         var fn = this.createFilterFn(property, value, anyMatch);
10847         return fn ? this.queryBy(fn) : this.data.clone();
10848     },
10849
10850     /**
10851      * Query by a function. The specified function will be called with each
10852      * record in this data source. If the function returns true the record is included
10853      * in the results.
10854      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10855      * @param {Object} scope (optional) The scope of the function (defaults to this)
10856       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10857      **/
10858     queryBy : function(fn, scope){
10859         var data = this.snapshot || this.data;
10860         return data.filterBy(fn, scope||this);
10861     },
10862
10863     /**
10864      * Collects unique values for a particular dataIndex from this store.
10865      * @param {String} dataIndex The property to collect
10866      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10867      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10868      * @return {Array} An array of the unique values
10869      **/
10870     collect : function(dataIndex, allowNull, bypassFilter){
10871         var d = (bypassFilter === true && this.snapshot) ?
10872                 this.snapshot.items : this.data.items;
10873         var v, sv, r = [], l = {};
10874         for(var i = 0, len = d.length; i < len; i++){
10875             v = d[i].data[dataIndex];
10876             sv = String(v);
10877             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10878                 l[sv] = true;
10879                 r[r.length] = v;
10880             }
10881         }
10882         return r;
10883     },
10884
10885     /**
10886      * Revert to a view of the Record cache with no filtering applied.
10887      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10888      */
10889     clearFilter : function(suppressEvent){
10890         if(this.snapshot && this.snapshot != this.data){
10891             this.data = this.snapshot;
10892             delete this.snapshot;
10893             if(suppressEvent !== true){
10894                 this.fireEvent("datachanged", this);
10895             }
10896         }
10897     },
10898
10899     // private
10900     afterEdit : function(record){
10901         if(this.modified.indexOf(record) == -1){
10902             this.modified.push(record);
10903         }
10904         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10905     },
10906     
10907     // private
10908     afterReject : function(record){
10909         this.modified.remove(record);
10910         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10911     },
10912
10913     // private
10914     afterCommit : function(record){
10915         this.modified.remove(record);
10916         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10917     },
10918
10919     /**
10920      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10921      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10922      */
10923     commitChanges : function(){
10924         var m = this.modified.slice(0);
10925         this.modified = [];
10926         for(var i = 0, len = m.length; i < len; i++){
10927             m[i].commit();
10928         }
10929     },
10930
10931     /**
10932      * Cancel outstanding changes on all changed records.
10933      */
10934     rejectChanges : function(){
10935         var m = this.modified.slice(0);
10936         this.modified = [];
10937         for(var i = 0, len = m.length; i < len; i++){
10938             m[i].reject();
10939         }
10940     },
10941
10942     onMetaChange : function(meta, rtype, o){
10943         this.recordType = rtype;
10944         this.fields = rtype.prototype.fields;
10945         delete this.snapshot;
10946         this.sortInfo = meta.sortInfo || this.sortInfo;
10947         this.modified = [];
10948         this.fireEvent('metachange', this, this.reader.meta);
10949     },
10950     
10951     moveIndex : function(data, type)
10952     {
10953         var index = this.indexOf(data);
10954         
10955         var newIndex = index + type;
10956         
10957         this.remove(data);
10958         
10959         this.insert(newIndex, data);
10960         
10961     }
10962 });/*
10963  * Based on:
10964  * Ext JS Library 1.1.1
10965  * Copyright(c) 2006-2007, Ext JS, LLC.
10966  *
10967  * Originally Released Under LGPL - original licence link has changed is not relivant.
10968  *
10969  * Fork - LGPL
10970  * <script type="text/javascript">
10971  */
10972
10973 /**
10974  * @class Roo.data.SimpleStore
10975  * @extends Roo.data.Store
10976  * Small helper class to make creating Stores from Array data easier.
10977  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10978  * @cfg {Array} fields An array of field definition objects, or field name strings.
10979  * @cfg {Array} data The multi-dimensional array of data
10980  * @constructor
10981  * @param {Object} config
10982  */
10983 Roo.data.SimpleStore = function(config){
10984     Roo.data.SimpleStore.superclass.constructor.call(this, {
10985         isLocal : true,
10986         reader: new Roo.data.ArrayReader({
10987                 id: config.id
10988             },
10989             Roo.data.Record.create(config.fields)
10990         ),
10991         proxy : new Roo.data.MemoryProxy(config.data)
10992     });
10993     this.load();
10994 };
10995 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10996  * Based on:
10997  * Ext JS Library 1.1.1
10998  * Copyright(c) 2006-2007, Ext JS, LLC.
10999  *
11000  * Originally Released Under LGPL - original licence link has changed is not relivant.
11001  *
11002  * Fork - LGPL
11003  * <script type="text/javascript">
11004  */
11005
11006 /**
11007 /**
11008  * @extends Roo.data.Store
11009  * @class Roo.data.JsonStore
11010  * Small helper class to make creating Stores for JSON data easier. <br/>
11011 <pre><code>
11012 var store = new Roo.data.JsonStore({
11013     url: 'get-images.php',
11014     root: 'images',
11015     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11016 });
11017 </code></pre>
11018  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11019  * JsonReader and HttpProxy (unless inline data is provided).</b>
11020  * @cfg {Array} fields An array of field definition objects, or field name strings.
11021  * @constructor
11022  * @param {Object} config
11023  */
11024 Roo.data.JsonStore = function(c){
11025     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11026         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11027         reader: new Roo.data.JsonReader(c, c.fields)
11028     }));
11029 };
11030 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11031  * Based on:
11032  * Ext JS Library 1.1.1
11033  * Copyright(c) 2006-2007, Ext JS, LLC.
11034  *
11035  * Originally Released Under LGPL - original licence link has changed is not relivant.
11036  *
11037  * Fork - LGPL
11038  * <script type="text/javascript">
11039  */
11040
11041  
11042 Roo.data.Field = function(config){
11043     if(typeof config == "string"){
11044         config = {name: config};
11045     }
11046     Roo.apply(this, config);
11047     
11048     if(!this.type){
11049         this.type = "auto";
11050     }
11051     
11052     var st = Roo.data.SortTypes;
11053     // named sortTypes are supported, here we look them up
11054     if(typeof this.sortType == "string"){
11055         this.sortType = st[this.sortType];
11056     }
11057     
11058     // set default sortType for strings and dates
11059     if(!this.sortType){
11060         switch(this.type){
11061             case "string":
11062                 this.sortType = st.asUCString;
11063                 break;
11064             case "date":
11065                 this.sortType = st.asDate;
11066                 break;
11067             default:
11068                 this.sortType = st.none;
11069         }
11070     }
11071
11072     // define once
11073     var stripRe = /[\$,%]/g;
11074
11075     // prebuilt conversion function for this field, instead of
11076     // switching every time we're reading a value
11077     if(!this.convert){
11078         var cv, dateFormat = this.dateFormat;
11079         switch(this.type){
11080             case "":
11081             case "auto":
11082             case undefined:
11083                 cv = function(v){ return v; };
11084                 break;
11085             case "string":
11086                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11087                 break;
11088             case "int":
11089                 cv = function(v){
11090                     return v !== undefined && v !== null && v !== '' ?
11091                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11092                     };
11093                 break;
11094             case "float":
11095                 cv = function(v){
11096                     return v !== undefined && v !== null && v !== '' ?
11097                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11098                     };
11099                 break;
11100             case "bool":
11101             case "boolean":
11102                 cv = function(v){ return v === true || v === "true" || v == 1; };
11103                 break;
11104             case "date":
11105                 cv = function(v){
11106                     if(!v){
11107                         return '';
11108                     }
11109                     if(v instanceof Date){
11110                         return v;
11111                     }
11112                     if(dateFormat){
11113                         if(dateFormat == "timestamp"){
11114                             return new Date(v*1000);
11115                         }
11116                         return Date.parseDate(v, dateFormat);
11117                     }
11118                     var parsed = Date.parse(v);
11119                     return parsed ? new Date(parsed) : null;
11120                 };
11121              break;
11122             
11123         }
11124         this.convert = cv;
11125     }
11126 };
11127
11128 Roo.data.Field.prototype = {
11129     dateFormat: null,
11130     defaultValue: "",
11131     mapping: null,
11132     sortType : null,
11133     sortDir : "ASC"
11134 };/*
11135  * Based on:
11136  * Ext JS Library 1.1.1
11137  * Copyright(c) 2006-2007, Ext JS, LLC.
11138  *
11139  * Originally Released Under LGPL - original licence link has changed is not relivant.
11140  *
11141  * Fork - LGPL
11142  * <script type="text/javascript">
11143  */
11144  
11145 // Base class for reading structured data from a data source.  This class is intended to be
11146 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11147
11148 /**
11149  * @class Roo.data.DataReader
11150  * Base class for reading structured data from a data source.  This class is intended to be
11151  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11152  */
11153
11154 Roo.data.DataReader = function(meta, recordType){
11155     
11156     this.meta = meta;
11157     
11158     this.recordType = recordType instanceof Array ? 
11159         Roo.data.Record.create(recordType) : recordType;
11160 };
11161
11162 Roo.data.DataReader.prototype = {
11163      /**
11164      * Create an empty record
11165      * @param {Object} data (optional) - overlay some values
11166      * @return {Roo.data.Record} record created.
11167      */
11168     newRow :  function(d) {
11169         var da =  {};
11170         this.recordType.prototype.fields.each(function(c) {
11171             switch( c.type) {
11172                 case 'int' : da[c.name] = 0; break;
11173                 case 'date' : da[c.name] = new Date(); break;
11174                 case 'float' : da[c.name] = 0.0; break;
11175                 case 'boolean' : da[c.name] = false; break;
11176                 default : da[c.name] = ""; break;
11177             }
11178             
11179         });
11180         return new this.recordType(Roo.apply(da, d));
11181     }
11182     
11183 };/*
11184  * Based on:
11185  * Ext JS Library 1.1.1
11186  * Copyright(c) 2006-2007, Ext JS, LLC.
11187  *
11188  * Originally Released Under LGPL - original licence link has changed is not relivant.
11189  *
11190  * Fork - LGPL
11191  * <script type="text/javascript">
11192  */
11193
11194 /**
11195  * @class Roo.data.DataProxy
11196  * @extends Roo.data.Observable
11197  * This class is an abstract base class for implementations which provide retrieval of
11198  * unformatted data objects.<br>
11199  * <p>
11200  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11201  * (of the appropriate type which knows how to parse the data object) to provide a block of
11202  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11203  * <p>
11204  * Custom implementations must implement the load method as described in
11205  * {@link Roo.data.HttpProxy#load}.
11206  */
11207 Roo.data.DataProxy = function(){
11208     this.addEvents({
11209         /**
11210          * @event beforeload
11211          * Fires before a network request is made to retrieve a data object.
11212          * @param {Object} This DataProxy object.
11213          * @param {Object} params The params parameter to the load function.
11214          */
11215         beforeload : true,
11216         /**
11217          * @event load
11218          * Fires before the load method's callback is called.
11219          * @param {Object} This DataProxy object.
11220          * @param {Object} o The data object.
11221          * @param {Object} arg The callback argument object passed to the load function.
11222          */
11223         load : true,
11224         /**
11225          * @event loadexception
11226          * Fires if an Exception occurs during data retrieval.
11227          * @param {Object} This DataProxy object.
11228          * @param {Object} o The data object.
11229          * @param {Object} arg The callback argument object passed to the load function.
11230          * @param {Object} e The Exception.
11231          */
11232         loadexception : true
11233     });
11234     Roo.data.DataProxy.superclass.constructor.call(this);
11235 };
11236
11237 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11238
11239     /**
11240      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11241      */
11242 /*
11243  * Based on:
11244  * Ext JS Library 1.1.1
11245  * Copyright(c) 2006-2007, Ext JS, LLC.
11246  *
11247  * Originally Released Under LGPL - original licence link has changed is not relivant.
11248  *
11249  * Fork - LGPL
11250  * <script type="text/javascript">
11251  */
11252 /**
11253  * @class Roo.data.MemoryProxy
11254  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11255  * to the Reader when its load method is called.
11256  * @constructor
11257  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11258  */
11259 Roo.data.MemoryProxy = function(data){
11260     if (data.data) {
11261         data = data.data;
11262     }
11263     Roo.data.MemoryProxy.superclass.constructor.call(this);
11264     this.data = data;
11265 };
11266
11267 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11268     
11269     /**
11270      * Load data from the requested source (in this case an in-memory
11271      * data object passed to the constructor), read the data object into
11272      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11273      * process that block using the passed callback.
11274      * @param {Object} params This parameter is not used by the MemoryProxy class.
11275      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11276      * object into a block of Roo.data.Records.
11277      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11278      * The function must be passed <ul>
11279      * <li>The Record block object</li>
11280      * <li>The "arg" argument from the load function</li>
11281      * <li>A boolean success indicator</li>
11282      * </ul>
11283      * @param {Object} scope The scope in which to call the callback
11284      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11285      */
11286     load : function(params, reader, callback, scope, arg){
11287         params = params || {};
11288         var result;
11289         try {
11290             result = reader.readRecords(this.data);
11291         }catch(e){
11292             this.fireEvent("loadexception", this, arg, null, e);
11293             callback.call(scope, null, arg, false);
11294             return;
11295         }
11296         callback.call(scope, result, arg, true);
11297     },
11298     
11299     // private
11300     update : function(params, records){
11301         
11302     }
11303 });/*
11304  * Based on:
11305  * Ext JS Library 1.1.1
11306  * Copyright(c) 2006-2007, Ext JS, LLC.
11307  *
11308  * Originally Released Under LGPL - original licence link has changed is not relivant.
11309  *
11310  * Fork - LGPL
11311  * <script type="text/javascript">
11312  */
11313 /**
11314  * @class Roo.data.HttpProxy
11315  * @extends Roo.data.DataProxy
11316  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11317  * configured to reference a certain URL.<br><br>
11318  * <p>
11319  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11320  * from which the running page was served.<br><br>
11321  * <p>
11322  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11323  * <p>
11324  * Be aware that to enable the browser to parse an XML document, the server must set
11325  * the Content-Type header in the HTTP response to "text/xml".
11326  * @constructor
11327  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11328  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11329  * will be used to make the request.
11330  */
11331 Roo.data.HttpProxy = function(conn){
11332     Roo.data.HttpProxy.superclass.constructor.call(this);
11333     // is conn a conn config or a real conn?
11334     this.conn = conn;
11335     this.useAjax = !conn || !conn.events;
11336   
11337 };
11338
11339 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11340     // thse are take from connection...
11341     
11342     /**
11343      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11344      */
11345     /**
11346      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11347      * extra parameters to each request made by this object. (defaults to undefined)
11348      */
11349     /**
11350      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11351      *  to each request made by this object. (defaults to undefined)
11352      */
11353     /**
11354      * @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)
11355      */
11356     /**
11357      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11358      */
11359      /**
11360      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11361      * @type Boolean
11362      */
11363   
11364
11365     /**
11366      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11367      * @type Boolean
11368      */
11369     /**
11370      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11371      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11372      * a finer-grained basis than the DataProxy events.
11373      */
11374     getConnection : function(){
11375         return this.useAjax ? Roo.Ajax : this.conn;
11376     },
11377
11378     /**
11379      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11380      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11381      * process that block using the passed callback.
11382      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11383      * for the request to the remote server.
11384      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11385      * object into a block of Roo.data.Records.
11386      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11387      * The function must be passed <ul>
11388      * <li>The Record block object</li>
11389      * <li>The "arg" argument from the load function</li>
11390      * <li>A boolean success indicator</li>
11391      * </ul>
11392      * @param {Object} scope The scope in which to call the callback
11393      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11394      */
11395     load : function(params, reader, callback, scope, arg){
11396         if(this.fireEvent("beforeload", this, params) !== false){
11397             var  o = {
11398                 params : params || {},
11399                 request: {
11400                     callback : callback,
11401                     scope : scope,
11402                     arg : arg
11403                 },
11404                 reader: reader,
11405                 callback : this.loadResponse,
11406                 scope: this
11407             };
11408             if(this.useAjax){
11409                 Roo.applyIf(o, this.conn);
11410                 if(this.activeRequest){
11411                     Roo.Ajax.abort(this.activeRequest);
11412                 }
11413                 this.activeRequest = Roo.Ajax.request(o);
11414             }else{
11415                 this.conn.request(o);
11416             }
11417         }else{
11418             callback.call(scope||this, null, arg, false);
11419         }
11420     },
11421
11422     // private
11423     loadResponse : function(o, success, response){
11424         delete this.activeRequest;
11425         if(!success){
11426             this.fireEvent("loadexception", this, o, response);
11427             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11428             return;
11429         }
11430         var result;
11431         try {
11432             result = o.reader.read(response);
11433         }catch(e){
11434             this.fireEvent("loadexception", this, o, response, e);
11435             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11436             return;
11437         }
11438         
11439         this.fireEvent("load", this, o, o.request.arg);
11440         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11441     },
11442
11443     // private
11444     update : function(dataSet){
11445
11446     },
11447
11448     // private
11449     updateResponse : function(dataSet){
11450
11451     }
11452 });/*
11453  * Based on:
11454  * Ext JS Library 1.1.1
11455  * Copyright(c) 2006-2007, Ext JS, LLC.
11456  *
11457  * Originally Released Under LGPL - original licence link has changed is not relivant.
11458  *
11459  * Fork - LGPL
11460  * <script type="text/javascript">
11461  */
11462
11463 /**
11464  * @class Roo.data.ScriptTagProxy
11465  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11466  * other than the originating domain of the running page.<br><br>
11467  * <p>
11468  * <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
11469  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11470  * <p>
11471  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11472  * source code that is used as the source inside a &lt;script> tag.<br><br>
11473  * <p>
11474  * In order for the browser to process the returned data, the server must wrap the data object
11475  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11476  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11477  * depending on whether the callback name was passed:
11478  * <p>
11479  * <pre><code>
11480 boolean scriptTag = false;
11481 String cb = request.getParameter("callback");
11482 if (cb != null) {
11483     scriptTag = true;
11484     response.setContentType("text/javascript");
11485 } else {
11486     response.setContentType("application/x-json");
11487 }
11488 Writer out = response.getWriter();
11489 if (scriptTag) {
11490     out.write(cb + "(");
11491 }
11492 out.print(dataBlock.toJsonString());
11493 if (scriptTag) {
11494     out.write(");");
11495 }
11496 </pre></code>
11497  *
11498  * @constructor
11499  * @param {Object} config A configuration object.
11500  */
11501 Roo.data.ScriptTagProxy = function(config){
11502     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11503     Roo.apply(this, config);
11504     this.head = document.getElementsByTagName("head")[0];
11505 };
11506
11507 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11508
11509 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11510     /**
11511      * @cfg {String} url The URL from which to request the data object.
11512      */
11513     /**
11514      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11515      */
11516     timeout : 30000,
11517     /**
11518      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11519      * the server the name of the callback function set up by the load call to process the returned data object.
11520      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11521      * javascript output which calls this named function passing the data object as its only parameter.
11522      */
11523     callbackParam : "callback",
11524     /**
11525      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11526      * name to the request.
11527      */
11528     nocache : true,
11529
11530     /**
11531      * Load data from the configured URL, read the data object into
11532      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11533      * process that block using the passed callback.
11534      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11535      * for the request to the remote server.
11536      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11537      * object into a block of Roo.data.Records.
11538      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11539      * The function must be passed <ul>
11540      * <li>The Record block object</li>
11541      * <li>The "arg" argument from the load function</li>
11542      * <li>A boolean success indicator</li>
11543      * </ul>
11544      * @param {Object} scope The scope in which to call the callback
11545      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11546      */
11547     load : function(params, reader, callback, scope, arg){
11548         if(this.fireEvent("beforeload", this, params) !== false){
11549
11550             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11551
11552             var url = this.url;
11553             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11554             if(this.nocache){
11555                 url += "&_dc=" + (new Date().getTime());
11556             }
11557             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11558             var trans = {
11559                 id : transId,
11560                 cb : "stcCallback"+transId,
11561                 scriptId : "stcScript"+transId,
11562                 params : params,
11563                 arg : arg,
11564                 url : url,
11565                 callback : callback,
11566                 scope : scope,
11567                 reader : reader
11568             };
11569             var conn = this;
11570
11571             window[trans.cb] = function(o){
11572                 conn.handleResponse(o, trans);
11573             };
11574
11575             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11576
11577             if(this.autoAbort !== false){
11578                 this.abort();
11579             }
11580
11581             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11582
11583             var script = document.createElement("script");
11584             script.setAttribute("src", url);
11585             script.setAttribute("type", "text/javascript");
11586             script.setAttribute("id", trans.scriptId);
11587             this.head.appendChild(script);
11588
11589             this.trans = trans;
11590         }else{
11591             callback.call(scope||this, null, arg, false);
11592         }
11593     },
11594
11595     // private
11596     isLoading : function(){
11597         return this.trans ? true : false;
11598     },
11599
11600     /**
11601      * Abort the current server request.
11602      */
11603     abort : function(){
11604         if(this.isLoading()){
11605             this.destroyTrans(this.trans);
11606         }
11607     },
11608
11609     // private
11610     destroyTrans : function(trans, isLoaded){
11611         this.head.removeChild(document.getElementById(trans.scriptId));
11612         clearTimeout(trans.timeoutId);
11613         if(isLoaded){
11614             window[trans.cb] = undefined;
11615             try{
11616                 delete window[trans.cb];
11617             }catch(e){}
11618         }else{
11619             // if hasn't been loaded, wait for load to remove it to prevent script error
11620             window[trans.cb] = function(){
11621                 window[trans.cb] = undefined;
11622                 try{
11623                     delete window[trans.cb];
11624                 }catch(e){}
11625             };
11626         }
11627     },
11628
11629     // private
11630     handleResponse : function(o, trans){
11631         this.trans = false;
11632         this.destroyTrans(trans, true);
11633         var result;
11634         try {
11635             result = trans.reader.readRecords(o);
11636         }catch(e){
11637             this.fireEvent("loadexception", this, o, trans.arg, e);
11638             trans.callback.call(trans.scope||window, null, trans.arg, false);
11639             return;
11640         }
11641         this.fireEvent("load", this, o, trans.arg);
11642         trans.callback.call(trans.scope||window, result, trans.arg, true);
11643     },
11644
11645     // private
11646     handleFailure : function(trans){
11647         this.trans = false;
11648         this.destroyTrans(trans, false);
11649         this.fireEvent("loadexception", this, null, trans.arg);
11650         trans.callback.call(trans.scope||window, null, trans.arg, false);
11651     }
11652 });/*
11653  * Based on:
11654  * Ext JS Library 1.1.1
11655  * Copyright(c) 2006-2007, Ext JS, LLC.
11656  *
11657  * Originally Released Under LGPL - original licence link has changed is not relivant.
11658  *
11659  * Fork - LGPL
11660  * <script type="text/javascript">
11661  */
11662
11663 /**
11664  * @class Roo.data.JsonReader
11665  * @extends Roo.data.DataReader
11666  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11667  * based on mappings in a provided Roo.data.Record constructor.
11668  * 
11669  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11670  * in the reply previously. 
11671  * 
11672  * <p>
11673  * Example code:
11674  * <pre><code>
11675 var RecordDef = Roo.data.Record.create([
11676     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11677     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11678 ]);
11679 var myReader = new Roo.data.JsonReader({
11680     totalProperty: "results",    // The property which contains the total dataset size (optional)
11681     root: "rows",                // The property which contains an Array of row objects
11682     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11683 }, RecordDef);
11684 </code></pre>
11685  * <p>
11686  * This would consume a JSON file like this:
11687  * <pre><code>
11688 { 'results': 2, 'rows': [
11689     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11690     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11691 }
11692 </code></pre>
11693  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11694  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11695  * paged from the remote server.
11696  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11697  * @cfg {String} root name of the property which contains the Array of row objects.
11698  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11699  * @cfg {Array} fields Array of field definition objects
11700  * @constructor
11701  * Create a new JsonReader
11702  * @param {Object} meta Metadata configuration options
11703  * @param {Object} recordType Either an Array of field definition objects,
11704  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11705  */
11706 Roo.data.JsonReader = function(meta, recordType){
11707     
11708     meta = meta || {};
11709     // set some defaults:
11710     Roo.applyIf(meta, {
11711         totalProperty: 'total',
11712         successProperty : 'success',
11713         root : 'data',
11714         id : 'id'
11715     });
11716     
11717     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11718 };
11719 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11720     
11721     /**
11722      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11723      * Used by Store query builder to append _requestMeta to params.
11724      * 
11725      */
11726     metaFromRemote : false,
11727     /**
11728      * This method is only used by a DataProxy which has retrieved data from a remote server.
11729      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11730      * @return {Object} data A data block which is used by an Roo.data.Store object as
11731      * a cache of Roo.data.Records.
11732      */
11733     read : function(response){
11734         var json = response.responseText;
11735        
11736         var o = /* eval:var:o */ eval("("+json+")");
11737         if(!o) {
11738             throw {message: "JsonReader.read: Json object not found"};
11739         }
11740         
11741         if(o.metaData){
11742             
11743             delete this.ef;
11744             this.metaFromRemote = true;
11745             this.meta = o.metaData;
11746             this.recordType = Roo.data.Record.create(o.metaData.fields);
11747             this.onMetaChange(this.meta, this.recordType, o);
11748         }
11749         return this.readRecords(o);
11750     },
11751
11752     // private function a store will implement
11753     onMetaChange : function(meta, recordType, o){
11754
11755     },
11756
11757     /**
11758          * @ignore
11759          */
11760     simpleAccess: function(obj, subsc) {
11761         return obj[subsc];
11762     },
11763
11764         /**
11765          * @ignore
11766          */
11767     getJsonAccessor: function(){
11768         var re = /[\[\.]/;
11769         return function(expr) {
11770             try {
11771                 return(re.test(expr))
11772                     ? new Function("obj", "return obj." + expr)
11773                     : function(obj){
11774                         return obj[expr];
11775                     };
11776             } catch(e){}
11777             return Roo.emptyFn;
11778         };
11779     }(),
11780
11781     /**
11782      * Create a data block containing Roo.data.Records from an XML document.
11783      * @param {Object} o An object which contains an Array of row objects in the property specified
11784      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11785      * which contains the total size of the dataset.
11786      * @return {Object} data A data block which is used by an Roo.data.Store object as
11787      * a cache of Roo.data.Records.
11788      */
11789     readRecords : function(o){
11790         /**
11791          * After any data loads, the raw JSON data is available for further custom processing.
11792          * @type Object
11793          */
11794         this.o = o;
11795         var s = this.meta, Record = this.recordType,
11796             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11797
11798 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11799         if (!this.ef) {
11800             if(s.totalProperty) {
11801                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11802                 }
11803                 if(s.successProperty) {
11804                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11805                 }
11806                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11807                 if (s.id) {
11808                         var g = this.getJsonAccessor(s.id);
11809                         this.getId = function(rec) {
11810                                 var r = g(rec);  
11811                                 return (r === undefined || r === "") ? null : r;
11812                         };
11813                 } else {
11814                         this.getId = function(){return null;};
11815                 }
11816             this.ef = [];
11817             for(var jj = 0; jj < fl; jj++){
11818                 f = fi[jj];
11819                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11820                 this.ef[jj] = this.getJsonAccessor(map);
11821             }
11822         }
11823
11824         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11825         if(s.totalProperty){
11826             var vt = parseInt(this.getTotal(o), 10);
11827             if(!isNaN(vt)){
11828                 totalRecords = vt;
11829             }
11830         }
11831         if(s.successProperty){
11832             var vs = this.getSuccess(o);
11833             if(vs === false || vs === 'false'){
11834                 success = false;
11835             }
11836         }
11837         var records = [];
11838         for(var i = 0; i < c; i++){
11839                 var n = root[i];
11840             var values = {};
11841             var id = this.getId(n);
11842             for(var j = 0; j < fl; j++){
11843                 f = fi[j];
11844             var v = this.ef[j](n);
11845             if (!f.convert) {
11846                 Roo.log('missing convert for ' + f.name);
11847                 Roo.log(f);
11848                 continue;
11849             }
11850             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11851             }
11852             var record = new Record(values, id);
11853             record.json = n;
11854             records[i] = record;
11855         }
11856         return {
11857             raw : o,
11858             success : success,
11859             records : records,
11860             totalRecords : totalRecords
11861         };
11862     }
11863 });/*
11864  * Based on:
11865  * Ext JS Library 1.1.1
11866  * Copyright(c) 2006-2007, Ext JS, LLC.
11867  *
11868  * Originally Released Under LGPL - original licence link has changed is not relivant.
11869  *
11870  * Fork - LGPL
11871  * <script type="text/javascript">
11872  */
11873
11874 /**
11875  * @class Roo.data.ArrayReader
11876  * @extends Roo.data.DataReader
11877  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11878  * Each element of that Array represents a row of data fields. The
11879  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11880  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11881  * <p>
11882  * Example code:.
11883  * <pre><code>
11884 var RecordDef = Roo.data.Record.create([
11885     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11886     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11887 ]);
11888 var myReader = new Roo.data.ArrayReader({
11889     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11890 }, RecordDef);
11891 </code></pre>
11892  * <p>
11893  * This would consume an Array like this:
11894  * <pre><code>
11895 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11896   </code></pre>
11897  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11898  * @constructor
11899  * Create a new JsonReader
11900  * @param {Object} meta Metadata configuration options.
11901  * @param {Object} recordType Either an Array of field definition objects
11902  * as specified to {@link Roo.data.Record#create},
11903  * or an {@link Roo.data.Record} object
11904  * created using {@link Roo.data.Record#create}.
11905  */
11906 Roo.data.ArrayReader = function(meta, recordType){
11907     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11908 };
11909
11910 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11911     /**
11912      * Create a data block containing Roo.data.Records from an XML document.
11913      * @param {Object} o An Array of row objects which represents the dataset.
11914      * @return {Object} data A data block which is used by an Roo.data.Store object as
11915      * a cache of Roo.data.Records.
11916      */
11917     readRecords : function(o){
11918         var sid = this.meta ? this.meta.id : null;
11919         var recordType = this.recordType, fields = recordType.prototype.fields;
11920         var records = [];
11921         var root = o;
11922             for(var i = 0; i < root.length; i++){
11923                     var n = root[i];
11924                 var values = {};
11925                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11926                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11927                 var f = fields.items[j];
11928                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11929                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11930                 v = f.convert(v);
11931                 values[f.name] = v;
11932             }
11933                 var record = new recordType(values, id);
11934                 record.json = n;
11935                 records[records.length] = record;
11936             }
11937             return {
11938                 records : records,
11939                 totalRecords : records.length
11940             };
11941     }
11942 });/*
11943  * - LGPL
11944  * * 
11945  */
11946
11947 /**
11948  * @class Roo.bootstrap.ComboBox
11949  * @extends Roo.bootstrap.TriggerField
11950  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11951  * @cfg {Boolean} append (true|false) default false
11952  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11953  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11954  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11955  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11956  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11957  * @cfg {Boolean} animate default true
11958  * @cfg {Boolean} emptyResultText only for touch device
11959  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11960  * @constructor
11961  * Create a new ComboBox.
11962  * @param {Object} config Configuration options
11963  */
11964 Roo.bootstrap.ComboBox = function(config){
11965     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11966     this.addEvents({
11967         /**
11968          * @event expand
11969          * Fires when the dropdown list is expanded
11970              * @param {Roo.bootstrap.ComboBox} combo This combo box
11971              */
11972         'expand' : true,
11973         /**
11974          * @event collapse
11975          * Fires when the dropdown list is collapsed
11976              * @param {Roo.bootstrap.ComboBox} combo This combo box
11977              */
11978         'collapse' : true,
11979         /**
11980          * @event beforeselect
11981          * Fires before a list item is selected. Return false to cancel the selection.
11982              * @param {Roo.bootstrap.ComboBox} combo This combo box
11983              * @param {Roo.data.Record} record The data record returned from the underlying store
11984              * @param {Number} index The index of the selected item in the dropdown list
11985              */
11986         'beforeselect' : true,
11987         /**
11988          * @event select
11989          * Fires when a list item is selected
11990              * @param {Roo.bootstrap.ComboBox} combo This combo box
11991              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11992              * @param {Number} index The index of the selected item in the dropdown list
11993              */
11994         'select' : true,
11995         /**
11996          * @event beforequery
11997          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11998          * The event object passed has these properties:
11999              * @param {Roo.bootstrap.ComboBox} combo This combo box
12000              * @param {String} query The query
12001              * @param {Boolean} forceAll true to force "all" query
12002              * @param {Boolean} cancel true to cancel the query
12003              * @param {Object} e The query event object
12004              */
12005         'beforequery': true,
12006          /**
12007          * @event add
12008          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12009              * @param {Roo.bootstrap.ComboBox} combo This combo box
12010              */
12011         'add' : true,
12012         /**
12013          * @event edit
12014          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12015              * @param {Roo.bootstrap.ComboBox} combo This combo box
12016              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12017              */
12018         'edit' : true,
12019         /**
12020          * @event remove
12021          * Fires when the remove value from the combobox array
12022              * @param {Roo.bootstrap.ComboBox} combo This combo box
12023              */
12024         'remove' : true,
12025         /**
12026          * @event afterremove
12027          * Fires when the remove value from the combobox array
12028              * @param {Roo.bootstrap.ComboBox} combo This combo box
12029              */
12030         'afterremove' : true,
12031         /**
12032          * @event specialfilter
12033          * Fires when specialfilter
12034             * @param {Roo.bootstrap.ComboBox} combo This combo box
12035             */
12036         'specialfilter' : true,
12037         /**
12038          * @event tick
12039          * Fires when tick the element
12040             * @param {Roo.bootstrap.ComboBox} combo This combo box
12041             */
12042         'tick' : true,
12043         /**
12044          * @event touchviewdisplay
12045          * Fires when touch view require special display (default is using displayField)
12046             * @param {Roo.bootstrap.ComboBox} combo This combo box
12047             * @param {Object} cfg set html .
12048             */
12049         'touchviewdisplay' : true
12050         
12051     });
12052     
12053     this.item = [];
12054     this.tickItems = [];
12055     
12056     this.selectedIndex = -1;
12057     if(this.mode == 'local'){
12058         if(config.queryDelay === undefined){
12059             this.queryDelay = 10;
12060         }
12061         if(config.minChars === undefined){
12062             this.minChars = 0;
12063         }
12064     }
12065 };
12066
12067 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12068      
12069     /**
12070      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12071      * rendering into an Roo.Editor, defaults to false)
12072      */
12073     /**
12074      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12075      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12076      */
12077     /**
12078      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12079      */
12080     /**
12081      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12082      * the dropdown list (defaults to undefined, with no header element)
12083      */
12084
12085      /**
12086      * @cfg {String/Roo.Template} tpl The template to use to render the output
12087      */
12088      
12089      /**
12090      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12091      */
12092     listWidth: undefined,
12093     /**
12094      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12095      * mode = 'remote' or 'text' if mode = 'local')
12096      */
12097     displayField: undefined,
12098     
12099     /**
12100      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12101      * mode = 'remote' or 'value' if mode = 'local'). 
12102      * Note: use of a valueField requires the user make a selection
12103      * in order for a value to be mapped.
12104      */
12105     valueField: undefined,
12106     /**
12107      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12108      */
12109     modalTitle : '',
12110     
12111     /**
12112      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12113      * field's data value (defaults to the underlying DOM element's name)
12114      */
12115     hiddenName: undefined,
12116     /**
12117      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12118      */
12119     listClass: '',
12120     /**
12121      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12122      */
12123     selectedClass: 'active',
12124     
12125     /**
12126      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12127      */
12128     shadow:'sides',
12129     /**
12130      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12131      * anchor positions (defaults to 'tl-bl')
12132      */
12133     listAlign: 'tl-bl?',
12134     /**
12135      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12136      */
12137     maxHeight: 300,
12138     /**
12139      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12140      * query specified by the allQuery config option (defaults to 'query')
12141      */
12142     triggerAction: 'query',
12143     /**
12144      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12145      * (defaults to 4, does not apply if editable = false)
12146      */
12147     minChars : 4,
12148     /**
12149      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12150      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12151      */
12152     typeAhead: false,
12153     /**
12154      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12155      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12156      */
12157     queryDelay: 500,
12158     /**
12159      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12160      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12161      */
12162     pageSize: 0,
12163     /**
12164      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12165      * when editable = true (defaults to false)
12166      */
12167     selectOnFocus:false,
12168     /**
12169      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12170      */
12171     queryParam: 'query',
12172     /**
12173      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12174      * when mode = 'remote' (defaults to 'Loading...')
12175      */
12176     loadingText: 'Loading...',
12177     /**
12178      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12179      */
12180     resizable: false,
12181     /**
12182      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12183      */
12184     handleHeight : 8,
12185     /**
12186      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12187      * traditional select (defaults to true)
12188      */
12189     editable: true,
12190     /**
12191      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12192      */
12193     allQuery: '',
12194     /**
12195      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12196      */
12197     mode: 'remote',
12198     /**
12199      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12200      * listWidth has a higher value)
12201      */
12202     minListWidth : 70,
12203     /**
12204      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12205      * allow the user to set arbitrary text into the field (defaults to false)
12206      */
12207     forceSelection:false,
12208     /**
12209      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12210      * if typeAhead = true (defaults to 250)
12211      */
12212     typeAheadDelay : 250,
12213     /**
12214      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12215      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12216      */
12217     valueNotFoundText : undefined,
12218     /**
12219      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12220      */
12221     blockFocus : false,
12222     
12223     /**
12224      * @cfg {Boolean} disableClear Disable showing of clear button.
12225      */
12226     disableClear : false,
12227     /**
12228      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12229      */
12230     alwaysQuery : false,
12231     
12232     /**
12233      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12234      */
12235     multiple : false,
12236     
12237     /**
12238      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12239      */
12240     invalidClass : "has-warning",
12241     
12242     /**
12243      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12244      */
12245     validClass : "has-success",
12246     
12247     /**
12248      * @cfg {Boolean} specialFilter (true|false) special filter default false
12249      */
12250     specialFilter : false,
12251     
12252     /**
12253      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12254      */
12255     mobileTouchView : true,
12256     
12257     /**
12258      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12259      */
12260     useNativeIOS : false,
12261     
12262     ios_options : false,
12263     
12264     //private
12265     addicon : false,
12266     editicon: false,
12267     
12268     page: 0,
12269     hasQuery: false,
12270     append: false,
12271     loadNext: false,
12272     autoFocus : true,
12273     tickable : false,
12274     btnPosition : 'right',
12275     triggerList : true,
12276     showToggleBtn : true,
12277     animate : true,
12278     emptyResultText: 'Empty',
12279     triggerText : 'Select',
12280     
12281     // element that contains real text value.. (when hidden is used..)
12282     
12283     getAutoCreate : function()
12284     {
12285         var cfg = false;
12286         
12287         /*
12288          * Render classic select for iso
12289          */
12290         
12291         if(Roo.isIOS && this.useNativeIOS){
12292             cfg = this.getAutoCreateNativeIOS();
12293             return cfg;
12294         }
12295         
12296         /*
12297          * Touch Devices
12298          */
12299         
12300         if(Roo.isTouch && this.mobileTouchView){
12301             cfg = this.getAutoCreateTouchView();
12302             return cfg;;
12303         }
12304         
12305         /*
12306          *  Normal ComboBox
12307          */
12308         if(!this.tickable){
12309             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12310             return cfg;
12311         }
12312         
12313         /*
12314          *  ComboBox with tickable selections
12315          */
12316              
12317         var align = this.labelAlign || this.parentLabelAlign();
12318         
12319         cfg = {
12320             cls : 'form-group roo-combobox-tickable' //input-group
12321         };
12322         
12323         var buttons = {
12324             tag : 'div',
12325             cls : 'tickable-buttons',
12326             cn : [
12327                 {
12328                     tag : 'button',
12329                     type : 'button',
12330                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12331                     html : this.triggerText
12332                 },
12333                 {
12334                     tag : 'button',
12335                     type : 'button',
12336                     name : 'ok',
12337                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12338                     html : 'Done'
12339                 },
12340                 {
12341                     tag : 'button',
12342                     type : 'button',
12343                     name : 'cancel',
12344                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12345                     html : 'Cancel'
12346                 }
12347             ]
12348         };
12349         
12350         if(this.editable){
12351             buttons.cn.unshift({
12352                 tag: 'input',
12353                 cls: 'roo-select2-search-field-input'
12354             });
12355         }
12356         
12357         var _this = this;
12358         
12359         Roo.each(buttons.cn, function(c){
12360             if (_this.size) {
12361                 c.cls += ' btn-' + _this.size;
12362             }
12363
12364             if (_this.disabled) {
12365                 c.disabled = true;
12366             }
12367         });
12368         
12369         var box = {
12370             tag: 'div',
12371             cn: [
12372                 {
12373                     tag: 'input',
12374                     type : 'hidden',
12375                     cls: 'form-hidden-field'
12376                 },
12377                 {
12378                     tag: 'ul',
12379                     cls: 'roo-select2-choices',
12380                     cn:[
12381                         {
12382                             tag: 'li',
12383                             cls: 'roo-select2-search-field',
12384                             cn: [
12385
12386                                 buttons
12387                             ]
12388                         }
12389                     ]
12390                 }
12391             ]
12392         };
12393         
12394         var combobox = {
12395             cls: 'roo-select2-container input-group roo-select2-container-multi',
12396             cn: [
12397                 box
12398 //                {
12399 //                    tag: 'ul',
12400 //                    cls: 'typeahead typeahead-long dropdown-menu',
12401 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12402 //                }
12403             ]
12404         };
12405         
12406         if(this.hasFeedback && !this.allowBlank){
12407             
12408             var feedback = {
12409                 tag: 'span',
12410                 cls: 'glyphicon form-control-feedback'
12411             };
12412
12413             combobox.cn.push(feedback);
12414         }
12415         
12416         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12417             
12418 //                Roo.log("left and has label");
12419             cfg.cn = [
12420                 {
12421                     tag : 'i',
12422                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12423                     tooltip : 'This field is required'
12424                 },
12425                 {
12426                     tag: 'label',
12427                     'for' :  id,
12428                     cls : 'control-label col-sm-' + this.labelWidth,
12429                     html : this.fieldLabel
12430
12431                 },
12432                 {
12433                     cls : "col-sm-" + (12 - this.labelWidth), 
12434                     cn: [
12435                         combobox
12436                     ]
12437                 }
12438
12439             ];
12440
12441             if(this.indicatorpos == 'right'){
12442                 
12443                 cfg.cn = [
12444                     {
12445                         tag: 'label',
12446                         'for' :  id,
12447                         cls : 'control-label col-sm-' + this.labelWidth,
12448                         html : this.fieldLabel
12449
12450                     },
12451                     {
12452                         tag : 'i',
12453                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12454                         tooltip : 'This field is required'
12455                     },
12456                     {
12457                         cls : "col-sm-" + (12 - this.labelWidth), 
12458                         cn: [
12459                             combobox
12460                         ]
12461                     }
12462
12463                 ];
12464             
12465             }
12466                 
12467                 
12468         } else if ( this.fieldLabel.length) {
12469 //                Roo.log(" label");
12470                  cfg.cn = [
12471                     {
12472                         tag : 'i',
12473                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12474                         tooltip : 'This field is required'
12475                     },
12476                     {
12477                         tag: 'label',
12478                         //cls : 'input-group-addon',
12479                         html : this.fieldLabel
12480                         
12481                     },
12482                     
12483                     combobox
12484                     
12485                 ];
12486                 
12487                 if(this.indicatorpos == 'right'){
12488                     
12489                     cfg.cn = [
12490                         {
12491                             tag: 'label',
12492                             //cls : 'input-group-addon',
12493                             html : this.fieldLabel
12494
12495                         },
12496                         
12497                         {
12498                             tag : 'i',
12499                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12500                             tooltip : 'This field is required'
12501                         },
12502                         
12503                         combobox
12504
12505                     ];
12506                 
12507                 }
12508
12509         } else {
12510             
12511 //                Roo.log(" no label && no align");
12512                 cfg = combobox
12513                      
12514                 
12515         }
12516          
12517         var settings=this;
12518         ['xs','sm','md','lg'].map(function(size){
12519             if (settings[size]) {
12520                 cfg.cls += ' col-' + size + '-' + settings[size];
12521             }
12522         });
12523         
12524         return cfg;
12525         
12526     },
12527     
12528     _initEventsCalled : false,
12529     
12530     // private
12531     initEvents: function()
12532     {   
12533         if (this._initEventsCalled) { // as we call render... prevent looping...
12534             return;
12535         }
12536         this._initEventsCalled = true;
12537         
12538         if (!this.store) {
12539             throw "can not find store for combo";
12540         }
12541         
12542         this.store = Roo.factory(this.store, Roo.data);
12543         
12544         // if we are building from html. then this element is so complex, that we can not really
12545         // use the rendered HTML.
12546         // so we have to trash and replace the previous code.
12547         if (Roo.XComponent.build_from_html) {
12548             
12549             // remove this element....
12550             var e = this.el.dom, k=0;
12551             while (e ) { e = e.previousSibling;  ++k;}
12552
12553             this.el.remove();
12554             
12555             this.el=false;
12556             this.rendered = false;
12557             
12558             this.render(this.parent().getChildContainer(true), k);
12559             
12560             
12561             
12562         }
12563         
12564         if(Roo.isIOS && this.useNativeIOS){
12565             this.initIOSView();
12566             return;
12567         }
12568         
12569         /*
12570          * Touch Devices
12571          */
12572         
12573         if(Roo.isTouch && this.mobileTouchView){
12574             this.initTouchView();
12575             return;
12576         }
12577         
12578         if(this.tickable){
12579             this.initTickableEvents();
12580             return;
12581         }
12582         
12583         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12584         
12585         if(this.hiddenName){
12586             
12587             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12588             
12589             this.hiddenField.dom.value =
12590                 this.hiddenValue !== undefined ? this.hiddenValue :
12591                 this.value !== undefined ? this.value : '';
12592
12593             // prevent input submission
12594             this.el.dom.removeAttribute('name');
12595             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12596              
12597              
12598         }
12599         //if(Roo.isGecko){
12600         //    this.el.dom.setAttribute('autocomplete', 'off');
12601         //}
12602         
12603         var cls = 'x-combo-list';
12604         
12605         //this.list = new Roo.Layer({
12606         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12607         //});
12608         
12609         var _this = this;
12610         
12611         (function(){
12612             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12613             _this.list.setWidth(lw);
12614         }).defer(100);
12615         
12616         this.list.on('mouseover', this.onViewOver, this);
12617         this.list.on('mousemove', this.onViewMove, this);
12618         
12619         this.list.on('scroll', this.onViewScroll, this);
12620         
12621         /*
12622         this.list.swallowEvent('mousewheel');
12623         this.assetHeight = 0;
12624
12625         if(this.title){
12626             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12627             this.assetHeight += this.header.getHeight();
12628         }
12629
12630         this.innerList = this.list.createChild({cls:cls+'-inner'});
12631         this.innerList.on('mouseover', this.onViewOver, this);
12632         this.innerList.on('mousemove', this.onViewMove, this);
12633         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12634         
12635         if(this.allowBlank && !this.pageSize && !this.disableClear){
12636             this.footer = this.list.createChild({cls:cls+'-ft'});
12637             this.pageTb = new Roo.Toolbar(this.footer);
12638            
12639         }
12640         if(this.pageSize){
12641             this.footer = this.list.createChild({cls:cls+'-ft'});
12642             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12643                     {pageSize: this.pageSize});
12644             
12645         }
12646         
12647         if (this.pageTb && this.allowBlank && !this.disableClear) {
12648             var _this = this;
12649             this.pageTb.add(new Roo.Toolbar.Fill(), {
12650                 cls: 'x-btn-icon x-btn-clear',
12651                 text: '&#160;',
12652                 handler: function()
12653                 {
12654                     _this.collapse();
12655                     _this.clearValue();
12656                     _this.onSelect(false, -1);
12657                 }
12658             });
12659         }
12660         if (this.footer) {
12661             this.assetHeight += this.footer.getHeight();
12662         }
12663         */
12664             
12665         if(!this.tpl){
12666             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12667         }
12668
12669         this.view = new Roo.View(this.list, this.tpl, {
12670             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12671         });
12672         //this.view.wrapEl.setDisplayed(false);
12673         this.view.on('click', this.onViewClick, this);
12674         
12675         
12676         
12677         this.store.on('beforeload', this.onBeforeLoad, this);
12678         this.store.on('load', this.onLoad, this);
12679         this.store.on('loadexception', this.onLoadException, this);
12680         /*
12681         if(this.resizable){
12682             this.resizer = new Roo.Resizable(this.list,  {
12683                pinned:true, handles:'se'
12684             });
12685             this.resizer.on('resize', function(r, w, h){
12686                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12687                 this.listWidth = w;
12688                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12689                 this.restrictHeight();
12690             }, this);
12691             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12692         }
12693         */
12694         if(!this.editable){
12695             this.editable = true;
12696             this.setEditable(false);
12697         }
12698         
12699         /*
12700         
12701         if (typeof(this.events.add.listeners) != 'undefined') {
12702             
12703             this.addicon = this.wrap.createChild(
12704                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12705        
12706             this.addicon.on('click', function(e) {
12707                 this.fireEvent('add', this);
12708             }, this);
12709         }
12710         if (typeof(this.events.edit.listeners) != 'undefined') {
12711             
12712             this.editicon = this.wrap.createChild(
12713                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12714             if (this.addicon) {
12715                 this.editicon.setStyle('margin-left', '40px');
12716             }
12717             this.editicon.on('click', function(e) {
12718                 
12719                 // we fire even  if inothing is selected..
12720                 this.fireEvent('edit', this, this.lastData );
12721                 
12722             }, this);
12723         }
12724         */
12725         
12726         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12727             "up" : function(e){
12728                 this.inKeyMode = true;
12729                 this.selectPrev();
12730             },
12731
12732             "down" : function(e){
12733                 if(!this.isExpanded()){
12734                     this.onTriggerClick();
12735                 }else{
12736                     this.inKeyMode = true;
12737                     this.selectNext();
12738                 }
12739             },
12740
12741             "enter" : function(e){
12742 //                this.onViewClick();
12743                 //return true;
12744                 this.collapse();
12745                 
12746                 if(this.fireEvent("specialkey", this, e)){
12747                     this.onViewClick(false);
12748                 }
12749                 
12750                 return true;
12751             },
12752
12753             "esc" : function(e){
12754                 this.collapse();
12755             },
12756
12757             "tab" : function(e){
12758                 this.collapse();
12759                 
12760                 if(this.fireEvent("specialkey", this, e)){
12761                     this.onViewClick(false);
12762                 }
12763                 
12764                 return true;
12765             },
12766
12767             scope : this,
12768
12769             doRelay : function(foo, bar, hname){
12770                 if(hname == 'down' || this.scope.isExpanded()){
12771                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12772                 }
12773                 return true;
12774             },
12775
12776             forceKeyDown: true
12777         });
12778         
12779         
12780         this.queryDelay = Math.max(this.queryDelay || 10,
12781                 this.mode == 'local' ? 10 : 250);
12782         
12783         
12784         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12785         
12786         if(this.typeAhead){
12787             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12788         }
12789         if(this.editable !== false){
12790             this.inputEl().on("keyup", this.onKeyUp, this);
12791         }
12792         if(this.forceSelection){
12793             this.inputEl().on('blur', this.doForce, this);
12794         }
12795         
12796         if(this.multiple){
12797             this.choices = this.el.select('ul.roo-select2-choices', true).first();
12798             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12799         }
12800     },
12801     
12802     initTickableEvents: function()
12803     {   
12804         this.createList();
12805         
12806         if(this.hiddenName){
12807             
12808             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12809             
12810             this.hiddenField.dom.value =
12811                 this.hiddenValue !== undefined ? this.hiddenValue :
12812                 this.value !== undefined ? this.value : '';
12813
12814             // prevent input submission
12815             this.el.dom.removeAttribute('name');
12816             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12817              
12818              
12819         }
12820         
12821 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12822         
12823         this.choices = this.el.select('ul.roo-select2-choices', true).first();
12824         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12825         if(this.triggerList){
12826             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12827         }
12828          
12829         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12830         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12831         
12832         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12833         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12834         
12835         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12836         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12837         
12838         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12839         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12840         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12841         
12842         this.okBtn.hide();
12843         this.cancelBtn.hide();
12844         
12845         var _this = this;
12846         
12847         (function(){
12848             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12849             _this.list.setWidth(lw);
12850         }).defer(100);
12851         
12852         this.list.on('mouseover', this.onViewOver, this);
12853         this.list.on('mousemove', this.onViewMove, this);
12854         
12855         this.list.on('scroll', this.onViewScroll, this);
12856         
12857         if(!this.tpl){
12858             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>';
12859         }
12860
12861         this.view = new Roo.View(this.list, this.tpl, {
12862             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12863         });
12864         
12865         //this.view.wrapEl.setDisplayed(false);
12866         this.view.on('click', this.onViewClick, this);
12867         
12868         
12869         
12870         this.store.on('beforeload', this.onBeforeLoad, this);
12871         this.store.on('load', this.onLoad, this);
12872         this.store.on('loadexception', this.onLoadException, this);
12873         
12874         if(this.editable){
12875             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12876                 "up" : function(e){
12877                     this.inKeyMode = true;
12878                     this.selectPrev();
12879                 },
12880
12881                 "down" : function(e){
12882                     this.inKeyMode = true;
12883                     this.selectNext();
12884                 },
12885
12886                 "enter" : function(e){
12887                     if(this.fireEvent("specialkey", this, e)){
12888                         this.onViewClick(false);
12889                     }
12890                     
12891                     return true;
12892                 },
12893
12894                 "esc" : function(e){
12895                     this.onTickableFooterButtonClick(e, false, false);
12896                 },
12897
12898                 "tab" : function(e){
12899                     this.fireEvent("specialkey", this, e);
12900                     
12901                     this.onTickableFooterButtonClick(e, false, false);
12902                     
12903                     return true;
12904                 },
12905
12906                 scope : this,
12907
12908                 doRelay : function(e, fn, key){
12909                     if(this.scope.isExpanded()){
12910                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12911                     }
12912                     return true;
12913                 },
12914
12915                 forceKeyDown: true
12916             });
12917         }
12918         
12919         this.queryDelay = Math.max(this.queryDelay || 10,
12920                 this.mode == 'local' ? 10 : 250);
12921         
12922         
12923         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12924         
12925         if(this.typeAhead){
12926             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12927         }
12928         
12929         if(this.editable !== false){
12930             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12931         }
12932         
12933     },
12934
12935     onDestroy : function(){
12936         if(this.view){
12937             this.view.setStore(null);
12938             this.view.el.removeAllListeners();
12939             this.view.el.remove();
12940             this.view.purgeListeners();
12941         }
12942         if(this.list){
12943             this.list.dom.innerHTML  = '';
12944         }
12945         
12946         if(this.store){
12947             this.store.un('beforeload', this.onBeforeLoad, this);
12948             this.store.un('load', this.onLoad, this);
12949             this.store.un('loadexception', this.onLoadException, this);
12950         }
12951         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12952     },
12953
12954     // private
12955     fireKey : function(e){
12956         if(e.isNavKeyPress() && !this.list.isVisible()){
12957             this.fireEvent("specialkey", this, e);
12958         }
12959     },
12960
12961     // private
12962     onResize: function(w, h){
12963 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12964 //        
12965 //        if(typeof w != 'number'){
12966 //            // we do not handle it!?!?
12967 //            return;
12968 //        }
12969 //        var tw = this.trigger.getWidth();
12970 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12971 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12972 //        var x = w - tw;
12973 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12974 //            
12975 //        //this.trigger.setStyle('left', x+'px');
12976 //        
12977 //        if(this.list && this.listWidth === undefined){
12978 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12979 //            this.list.setWidth(lw);
12980 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12981 //        }
12982         
12983     
12984         
12985     },
12986
12987     /**
12988      * Allow or prevent the user from directly editing the field text.  If false is passed,
12989      * the user will only be able to select from the items defined in the dropdown list.  This method
12990      * is the runtime equivalent of setting the 'editable' config option at config time.
12991      * @param {Boolean} value True to allow the user to directly edit the field text
12992      */
12993     setEditable : function(value){
12994         if(value == this.editable){
12995             return;
12996         }
12997         this.editable = value;
12998         if(!value){
12999             this.inputEl().dom.setAttribute('readOnly', true);
13000             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13001             this.inputEl().addClass('x-combo-noedit');
13002         }else{
13003             this.inputEl().dom.setAttribute('readOnly', false);
13004             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13005             this.inputEl().removeClass('x-combo-noedit');
13006         }
13007     },
13008
13009     // private
13010     
13011     onBeforeLoad : function(combo,opts){
13012         if(!this.hasFocus){
13013             return;
13014         }
13015          if (!opts.add) {
13016             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13017          }
13018         this.restrictHeight();
13019         this.selectedIndex = -1;
13020     },
13021
13022     // private
13023     onLoad : function(){
13024         
13025         this.hasQuery = false;
13026         
13027         if(!this.hasFocus){
13028             return;
13029         }
13030         
13031         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13032             this.loading.hide();
13033         }
13034              
13035         if(this.store.getCount() > 0){
13036             this.expand();
13037             this.restrictHeight();
13038             if(this.lastQuery == this.allQuery){
13039                 if(this.editable && !this.tickable){
13040                     this.inputEl().dom.select();
13041                 }
13042                 
13043                 if(
13044                     !this.selectByValue(this.value, true) &&
13045                     this.autoFocus && 
13046                     (
13047                         !this.store.lastOptions ||
13048                         typeof(this.store.lastOptions.add) == 'undefined' || 
13049                         this.store.lastOptions.add != true
13050                     )
13051                 ){
13052                     this.select(0, true);
13053                 }
13054             }else{
13055                 if(this.autoFocus){
13056                     this.selectNext();
13057                 }
13058                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13059                     this.taTask.delay(this.typeAheadDelay);
13060                 }
13061             }
13062         }else{
13063             this.onEmptyResults();
13064         }
13065         
13066         //this.el.focus();
13067     },
13068     // private
13069     onLoadException : function()
13070     {
13071         this.hasQuery = false;
13072         
13073         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13074             this.loading.hide();
13075         }
13076         
13077         if(this.tickable && this.editable){
13078             return;
13079         }
13080         
13081         this.collapse();
13082         // only causes errors at present
13083         //Roo.log(this.store.reader.jsonData);
13084         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13085             // fixme
13086             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13087         //}
13088         
13089         
13090     },
13091     // private
13092     onTypeAhead : function(){
13093         if(this.store.getCount() > 0){
13094             var r = this.store.getAt(0);
13095             var newValue = r.data[this.displayField];
13096             var len = newValue.length;
13097             var selStart = this.getRawValue().length;
13098             
13099             if(selStart != len){
13100                 this.setRawValue(newValue);
13101                 this.selectText(selStart, newValue.length);
13102             }
13103         }
13104     },
13105
13106     // private
13107     onSelect : function(record, index){
13108         
13109         if(this.fireEvent('beforeselect', this, record, index) !== false){
13110         
13111             this.setFromData(index > -1 ? record.data : false);
13112             
13113             this.collapse();
13114             this.fireEvent('select', this, record, index);
13115         }
13116     },
13117
13118     /**
13119      * Returns the currently selected field value or empty string if no value is set.
13120      * @return {String} value The selected value
13121      */
13122     getValue : function()
13123     {
13124         if(Roo.isIOS && this.useNativeIOS){
13125             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13126         }
13127         
13128         if(this.multiple){
13129             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13130         }
13131         
13132         if(this.valueField){
13133             return typeof this.value != 'undefined' ? this.value : '';
13134         }else{
13135             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13136         }
13137     },
13138     
13139     getRawValue : function()
13140     {
13141         if(Roo.isIOS && this.useNativeIOS){
13142             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13143         }
13144         
13145         var v = this.inputEl().getValue();
13146         
13147         return v;
13148     },
13149
13150     /**
13151      * Clears any text/value currently set in the field
13152      */
13153     clearValue : function(){
13154         
13155         if(this.hiddenField){
13156             this.hiddenField.dom.value = '';
13157         }
13158         this.value = '';
13159         this.setRawValue('');
13160         this.lastSelectionText = '';
13161         this.lastData = false;
13162         
13163         var close = this.closeTriggerEl();
13164         
13165         if(close){
13166             close.hide();
13167         }
13168         
13169         this.validate();
13170         
13171     },
13172
13173     /**
13174      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13175      * will be displayed in the field.  If the value does not match the data value of an existing item,
13176      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13177      * Otherwise the field will be blank (although the value will still be set).
13178      * @param {String} value The value to match
13179      */
13180     setValue : function(v)
13181     {
13182         if(Roo.isIOS && this.useNativeIOS){
13183             this.setIOSValue(v);
13184             return;
13185         }
13186         
13187         if(this.multiple){
13188             this.syncValue();
13189             return;
13190         }
13191         
13192         var text = v;
13193         if(this.valueField){
13194             var r = this.findRecord(this.valueField, v);
13195             if(r){
13196                 text = r.data[this.displayField];
13197             }else if(this.valueNotFoundText !== undefined){
13198                 text = this.valueNotFoundText;
13199             }
13200         }
13201         this.lastSelectionText = text;
13202         if(this.hiddenField){
13203             this.hiddenField.dom.value = v;
13204         }
13205         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13206         this.value = v;
13207         
13208         var close = this.closeTriggerEl();
13209         
13210         if(close){
13211             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13212         }
13213         
13214         this.validate();
13215     },
13216     /**
13217      * @property {Object} the last set data for the element
13218      */
13219     
13220     lastData : false,
13221     /**
13222      * Sets the value of the field based on a object which is related to the record format for the store.
13223      * @param {Object} value the value to set as. or false on reset?
13224      */
13225     setFromData : function(o){
13226         
13227         if(this.multiple){
13228             this.addItem(o);
13229             return;
13230         }
13231             
13232         var dv = ''; // display value
13233         var vv = ''; // value value..
13234         this.lastData = o;
13235         if (this.displayField) {
13236             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13237         } else {
13238             // this is an error condition!!!
13239             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13240         }
13241         
13242         if(this.valueField){
13243             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13244         }
13245         
13246         var close = this.closeTriggerEl();
13247         
13248         if(close){
13249             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13250         }
13251         
13252         if(this.hiddenField){
13253             this.hiddenField.dom.value = vv;
13254             
13255             this.lastSelectionText = dv;
13256             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13257             this.value = vv;
13258             return;
13259         }
13260         // no hidden field.. - we store the value in 'value', but still display
13261         // display field!!!!
13262         this.lastSelectionText = dv;
13263         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13264         this.value = vv;
13265         
13266         
13267         
13268     },
13269     // private
13270     reset : function(){
13271         // overridden so that last data is reset..
13272         
13273         if(this.multiple){
13274             this.clearItem();
13275             return;
13276         }
13277         
13278         this.setValue(this.originalValue);
13279         //this.clearInvalid();
13280         this.lastData = false;
13281         if (this.view) {
13282             this.view.clearSelections();
13283         }
13284         
13285         this.validate();
13286     },
13287     // private
13288     findRecord : function(prop, value){
13289         var record;
13290         if(this.store.getCount() > 0){
13291             this.store.each(function(r){
13292                 if(r.data[prop] == value){
13293                     record = r;
13294                     return false;
13295                 }
13296                 return true;
13297             });
13298         }
13299         return record;
13300     },
13301     
13302     getName: function()
13303     {
13304         // returns hidden if it's set..
13305         if (!this.rendered) {return ''};
13306         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13307         
13308     },
13309     // private
13310     onViewMove : function(e, t){
13311         this.inKeyMode = false;
13312     },
13313
13314     // private
13315     onViewOver : function(e, t){
13316         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13317             return;
13318         }
13319         var item = this.view.findItemFromChild(t);
13320         
13321         if(item){
13322             var index = this.view.indexOf(item);
13323             this.select(index, false);
13324         }
13325     },
13326
13327     // private
13328     onViewClick : function(view, doFocus, el, e)
13329     {
13330         var index = this.view.getSelectedIndexes()[0];
13331         
13332         var r = this.store.getAt(index);
13333         
13334         if(this.tickable){
13335             
13336             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13337                 return;
13338             }
13339             
13340             var rm = false;
13341             var _this = this;
13342             
13343             Roo.each(this.tickItems, function(v,k){
13344                 
13345                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13346                     Roo.log(v);
13347                     _this.tickItems.splice(k, 1);
13348                     
13349                     if(typeof(e) == 'undefined' && view == false){
13350                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13351                     }
13352                     
13353                     rm = true;
13354                     return;
13355                 }
13356             });
13357             
13358             if(rm){
13359                 return;
13360             }
13361             
13362             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13363                 this.tickItems.push(r.data);
13364             }
13365             
13366             if(typeof(e) == 'undefined' && view == false){
13367                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13368             }
13369                     
13370             return;
13371         }
13372         
13373         if(r){
13374             this.onSelect(r, index);
13375         }
13376         if(doFocus !== false && !this.blockFocus){
13377             this.inputEl().focus();
13378         }
13379     },
13380
13381     // private
13382     restrictHeight : function(){
13383         //this.innerList.dom.style.height = '';
13384         //var inner = this.innerList.dom;
13385         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13386         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13387         //this.list.beginUpdate();
13388         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13389         this.list.alignTo(this.inputEl(), this.listAlign);
13390         this.list.alignTo(this.inputEl(), this.listAlign);
13391         //this.list.endUpdate();
13392     },
13393
13394     // private
13395     onEmptyResults : function(){
13396         
13397         if(this.tickable && this.editable){
13398             this.restrictHeight();
13399             return;
13400         }
13401         
13402         this.collapse();
13403     },
13404
13405     /**
13406      * Returns true if the dropdown list is expanded, else false.
13407      */
13408     isExpanded : function(){
13409         return this.list.isVisible();
13410     },
13411
13412     /**
13413      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13414      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13415      * @param {String} value The data value of the item to select
13416      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13417      * selected item if it is not currently in view (defaults to true)
13418      * @return {Boolean} True if the value matched an item in the list, else false
13419      */
13420     selectByValue : function(v, scrollIntoView){
13421         if(v !== undefined && v !== null){
13422             var r = this.findRecord(this.valueField || this.displayField, v);
13423             if(r){
13424                 this.select(this.store.indexOf(r), scrollIntoView);
13425                 return true;
13426             }
13427         }
13428         return false;
13429     },
13430
13431     /**
13432      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13433      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13434      * @param {Number} index The zero-based index of the list item to select
13435      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13436      * selected item if it is not currently in view (defaults to true)
13437      */
13438     select : function(index, scrollIntoView){
13439         this.selectedIndex = index;
13440         this.view.select(index);
13441         if(scrollIntoView !== false){
13442             var el = this.view.getNode(index);
13443             /*
13444              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13445              */
13446             if(el){
13447                 this.list.scrollChildIntoView(el, false);
13448             }
13449         }
13450     },
13451
13452     // private
13453     selectNext : function(){
13454         var ct = this.store.getCount();
13455         if(ct > 0){
13456             if(this.selectedIndex == -1){
13457                 this.select(0);
13458             }else if(this.selectedIndex < ct-1){
13459                 this.select(this.selectedIndex+1);
13460             }
13461         }
13462     },
13463
13464     // private
13465     selectPrev : function(){
13466         var ct = this.store.getCount();
13467         if(ct > 0){
13468             if(this.selectedIndex == -1){
13469                 this.select(0);
13470             }else if(this.selectedIndex != 0){
13471                 this.select(this.selectedIndex-1);
13472             }
13473         }
13474     },
13475
13476     // private
13477     onKeyUp : function(e){
13478         if(this.editable !== false && !e.isSpecialKey()){
13479             this.lastKey = e.getKey();
13480             this.dqTask.delay(this.queryDelay);
13481         }
13482     },
13483
13484     // private
13485     validateBlur : function(){
13486         return !this.list || !this.list.isVisible();   
13487     },
13488
13489     // private
13490     initQuery : function(){
13491         
13492         var v = this.getRawValue();
13493         
13494         if(this.tickable && this.editable){
13495             v = this.tickableInputEl().getValue();
13496         }
13497         
13498         this.doQuery(v);
13499     },
13500
13501     // private
13502     doForce : function(){
13503         if(this.inputEl().dom.value.length > 0){
13504             this.inputEl().dom.value =
13505                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13506              
13507         }
13508     },
13509
13510     /**
13511      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13512      * query allowing the query action to be canceled if needed.
13513      * @param {String} query The SQL query to execute
13514      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13515      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13516      * saved in the current store (defaults to false)
13517      */
13518     doQuery : function(q, forceAll){
13519         
13520         if(q === undefined || q === null){
13521             q = '';
13522         }
13523         var qe = {
13524             query: q,
13525             forceAll: forceAll,
13526             combo: this,
13527             cancel:false
13528         };
13529         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13530             return false;
13531         }
13532         q = qe.query;
13533         
13534         forceAll = qe.forceAll;
13535         if(forceAll === true || (q.length >= this.minChars)){
13536             
13537             this.hasQuery = true;
13538             
13539             if(this.lastQuery != q || this.alwaysQuery){
13540                 this.lastQuery = q;
13541                 if(this.mode == 'local'){
13542                     this.selectedIndex = -1;
13543                     if(forceAll){
13544                         this.store.clearFilter();
13545                     }else{
13546                         
13547                         if(this.specialFilter){
13548                             this.fireEvent('specialfilter', this);
13549                             this.onLoad();
13550                             return;
13551                         }
13552                         
13553                         this.store.filter(this.displayField, q);
13554                     }
13555                     
13556                     this.store.fireEvent("datachanged", this.store);
13557                     
13558                     this.onLoad();
13559                     
13560                     
13561                 }else{
13562                     
13563                     this.store.baseParams[this.queryParam] = q;
13564                     
13565                     var options = {params : this.getParams(q)};
13566                     
13567                     if(this.loadNext){
13568                         options.add = true;
13569                         options.params.start = this.page * this.pageSize;
13570                     }
13571                     
13572                     this.store.load(options);
13573                     
13574                     /*
13575                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13576                      *  we should expand the list on onLoad
13577                      *  so command out it
13578                      */
13579 //                    this.expand();
13580                 }
13581             }else{
13582                 this.selectedIndex = -1;
13583                 this.onLoad();   
13584             }
13585         }
13586         
13587         this.loadNext = false;
13588     },
13589     
13590     // private
13591     getParams : function(q){
13592         var p = {};
13593         //p[this.queryParam] = q;
13594         
13595         if(this.pageSize){
13596             p.start = 0;
13597             p.limit = this.pageSize;
13598         }
13599         return p;
13600     },
13601
13602     /**
13603      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13604      */
13605     collapse : function(){
13606         if(!this.isExpanded()){
13607             return;
13608         }
13609         
13610         this.list.hide();
13611         
13612         if(this.tickable){
13613             this.hasFocus = false;
13614             this.okBtn.hide();
13615             this.cancelBtn.hide();
13616             this.trigger.show();
13617             
13618             if(this.editable){
13619                 this.tickableInputEl().dom.value = '';
13620                 this.tickableInputEl().blur();
13621             }
13622             
13623         }
13624         
13625         Roo.get(document).un('mousedown', this.collapseIf, this);
13626         Roo.get(document).un('mousewheel', this.collapseIf, this);
13627         if (!this.editable) {
13628             Roo.get(document).un('keydown', this.listKeyPress, this);
13629         }
13630         this.fireEvent('collapse', this);
13631         
13632         this.validate();
13633     },
13634
13635     // private
13636     collapseIf : function(e){
13637         var in_combo  = e.within(this.el);
13638         var in_list =  e.within(this.list);
13639         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13640         
13641         if (in_combo || in_list || is_list) {
13642             //e.stopPropagation();
13643             return;
13644         }
13645         
13646         if(this.tickable){
13647             this.onTickableFooterButtonClick(e, false, false);
13648         }
13649
13650         this.collapse();
13651         
13652     },
13653
13654     /**
13655      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13656      */
13657     expand : function(){
13658        
13659         if(this.isExpanded() || !this.hasFocus){
13660             return;
13661         }
13662         
13663         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13664         this.list.setWidth(lw);
13665         
13666         
13667          Roo.log('expand');
13668         
13669         this.list.show();
13670         
13671         this.restrictHeight();
13672         
13673         if(this.tickable){
13674             
13675             this.tickItems = Roo.apply([], this.item);
13676             
13677             this.okBtn.show();
13678             this.cancelBtn.show();
13679             this.trigger.hide();
13680             
13681             if(this.editable){
13682                 this.tickableInputEl().focus();
13683             }
13684             
13685         }
13686         
13687         Roo.get(document).on('mousedown', this.collapseIf, this);
13688         Roo.get(document).on('mousewheel', this.collapseIf, this);
13689         if (!this.editable) {
13690             Roo.get(document).on('keydown', this.listKeyPress, this);
13691         }
13692         
13693         this.fireEvent('expand', this);
13694     },
13695
13696     // private
13697     // Implements the default empty TriggerField.onTriggerClick function
13698     onTriggerClick : function(e)
13699     {
13700         Roo.log('trigger click');
13701         
13702         if(this.disabled || !this.triggerList){
13703             return;
13704         }
13705         
13706         this.page = 0;
13707         this.loadNext = false;
13708         
13709         if(this.isExpanded()){
13710             this.collapse();
13711             if (!this.blockFocus) {
13712                 this.inputEl().focus();
13713             }
13714             
13715         }else {
13716             this.hasFocus = true;
13717             if(this.triggerAction == 'all') {
13718                 this.doQuery(this.allQuery, true);
13719             } else {
13720                 this.doQuery(this.getRawValue());
13721             }
13722             if (!this.blockFocus) {
13723                 this.inputEl().focus();
13724             }
13725         }
13726     },
13727     
13728     onTickableTriggerClick : function(e)
13729     {
13730         if(this.disabled){
13731             return;
13732         }
13733         
13734         this.page = 0;
13735         this.loadNext = false;
13736         this.hasFocus = true;
13737         
13738         if(this.triggerAction == 'all') {
13739             this.doQuery(this.allQuery, true);
13740         } else {
13741             this.doQuery(this.getRawValue());
13742         }
13743     },
13744     
13745     onSearchFieldClick : function(e)
13746     {
13747         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13748             this.onTickableFooterButtonClick(e, false, false);
13749             return;
13750         }
13751         
13752         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13753             return;
13754         }
13755         
13756         this.page = 0;
13757         this.loadNext = false;
13758         this.hasFocus = true;
13759         
13760         if(this.triggerAction == 'all') {
13761             this.doQuery(this.allQuery, true);
13762         } else {
13763             this.doQuery(this.getRawValue());
13764         }
13765     },
13766     
13767     listKeyPress : function(e)
13768     {
13769         //Roo.log('listkeypress');
13770         // scroll to first matching element based on key pres..
13771         if (e.isSpecialKey()) {
13772             return false;
13773         }
13774         var k = String.fromCharCode(e.getKey()).toUpperCase();
13775         //Roo.log(k);
13776         var match  = false;
13777         var csel = this.view.getSelectedNodes();
13778         var cselitem = false;
13779         if (csel.length) {
13780             var ix = this.view.indexOf(csel[0]);
13781             cselitem  = this.store.getAt(ix);
13782             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13783                 cselitem = false;
13784             }
13785             
13786         }
13787         
13788         this.store.each(function(v) { 
13789             if (cselitem) {
13790                 // start at existing selection.
13791                 if (cselitem.id == v.id) {
13792                     cselitem = false;
13793                 }
13794                 return true;
13795             }
13796                 
13797             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13798                 match = this.store.indexOf(v);
13799                 return false;
13800             }
13801             return true;
13802         }, this);
13803         
13804         if (match === false) {
13805             return true; // no more action?
13806         }
13807         // scroll to?
13808         this.view.select(match);
13809         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13810         sn.scrollIntoView(sn.dom.parentNode, false);
13811     },
13812     
13813     onViewScroll : function(e, t){
13814         
13815         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){
13816             return;
13817         }
13818         
13819         this.hasQuery = true;
13820         
13821         this.loading = this.list.select('.loading', true).first();
13822         
13823         if(this.loading === null){
13824             this.list.createChild({
13825                 tag: 'div',
13826                 cls: 'loading roo-select2-more-results roo-select2-active',
13827                 html: 'Loading more results...'
13828             });
13829             
13830             this.loading = this.list.select('.loading', true).first();
13831             
13832             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13833             
13834             this.loading.hide();
13835         }
13836         
13837         this.loading.show();
13838         
13839         var _combo = this;
13840         
13841         this.page++;
13842         this.loadNext = true;
13843         
13844         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13845         
13846         return;
13847     },
13848     
13849     addItem : function(o)
13850     {   
13851         var dv = ''; // display value
13852         
13853         if (this.displayField) {
13854             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13855         } else {
13856             // this is an error condition!!!
13857             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13858         }
13859         
13860         if(!dv.length){
13861             return;
13862         }
13863         
13864         var choice = this.choices.createChild({
13865             tag: 'li',
13866             cls: 'roo-select2-search-choice',
13867             cn: [
13868                 {
13869                     tag: 'div',
13870                     html: dv
13871                 },
13872                 {
13873                     tag: 'a',
13874                     href: '#',
13875                     cls: 'roo-select2-search-choice-close',
13876                     tabindex: '-1'
13877                 }
13878             ]
13879             
13880         }, this.searchField);
13881         
13882         var close = choice.select('a.roo-select2-search-choice-close', true).first();
13883         
13884         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13885         
13886         this.item.push(o);
13887         
13888         this.lastData = o;
13889         
13890         this.syncValue();
13891         
13892         this.inputEl().dom.value = '';
13893         
13894         this.validate();
13895     },
13896     
13897     onRemoveItem : function(e, _self, o)
13898     {
13899         e.preventDefault();
13900         
13901         this.lastItem = Roo.apply([], this.item);
13902         
13903         var index = this.item.indexOf(o.data) * 1;
13904         
13905         if( index < 0){
13906             Roo.log('not this item?!');
13907             return;
13908         }
13909         
13910         this.item.splice(index, 1);
13911         o.item.remove();
13912         
13913         this.syncValue();
13914         
13915         this.fireEvent('remove', this, e);
13916         
13917         this.validate();
13918         
13919     },
13920     
13921     syncValue : function()
13922     {
13923         if(!this.item.length){
13924             this.clearValue();
13925             return;
13926         }
13927             
13928         var value = [];
13929         var _this = this;
13930         Roo.each(this.item, function(i){
13931             if(_this.valueField){
13932                 value.push(i[_this.valueField]);
13933                 return;
13934             }
13935
13936             value.push(i);
13937         });
13938
13939         this.value = value.join(',');
13940
13941         if(this.hiddenField){
13942             this.hiddenField.dom.value = this.value;
13943         }
13944         
13945         this.store.fireEvent("datachanged", this.store);
13946         
13947         this.validate();
13948     },
13949     
13950     clearItem : function()
13951     {
13952         if(!this.multiple){
13953             return;
13954         }
13955         
13956         this.item = [];
13957         
13958         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13959            c.remove();
13960         });
13961         
13962         this.syncValue();
13963         
13964         this.validate();
13965         
13966         if(this.tickable && !Roo.isTouch){
13967             this.view.refresh();
13968         }
13969     },
13970     
13971     inputEl: function ()
13972     {
13973         if(Roo.isIOS && this.useNativeIOS){
13974             return this.el.select('select.roo-ios-select', true).first();
13975         }
13976         
13977         if(Roo.isTouch && this.mobileTouchView){
13978             return this.el.select('input.form-control',true).first();
13979         }
13980         
13981         if(this.tickable){
13982             return this.searchField;
13983         }
13984         
13985         return this.el.select('input.form-control',true).first();
13986     },
13987     
13988     onTickableFooterButtonClick : function(e, btn, el)
13989     {
13990         e.preventDefault();
13991         
13992         this.lastItem = Roo.apply([], this.item);
13993         
13994         if(btn && btn.name == 'cancel'){
13995             this.tickItems = Roo.apply([], this.item);
13996             this.collapse();
13997             return;
13998         }
13999         
14000         this.clearItem();
14001         
14002         var _this = this;
14003         
14004         Roo.each(this.tickItems, function(o){
14005             _this.addItem(o);
14006         });
14007         
14008         this.collapse();
14009         
14010     },
14011     
14012     validate : function()
14013     {
14014         var v = this.getRawValue();
14015         
14016         if(this.multiple){
14017             v = this.getValue();
14018         }
14019         
14020         if(this.disabled || this.allowBlank || v.length){
14021             this.markValid();
14022             return true;
14023         }
14024         
14025         this.markInvalid();
14026         return false;
14027     },
14028     
14029     tickableInputEl : function()
14030     {
14031         if(!this.tickable || !this.editable){
14032             return this.inputEl();
14033         }
14034         
14035         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14036     },
14037     
14038     
14039     getAutoCreateTouchView : function()
14040     {
14041         var id = Roo.id();
14042         
14043         var cfg = {
14044             cls: 'form-group' //input-group
14045         };
14046         
14047         var input =  {
14048             tag: 'input',
14049             id : id,
14050             type : this.inputType,
14051             cls : 'form-control x-combo-noedit',
14052             autocomplete: 'new-password',
14053             placeholder : this.placeholder || '',
14054             readonly : true
14055         };
14056         
14057         if (this.name) {
14058             input.name = this.name;
14059         }
14060         
14061         if (this.size) {
14062             input.cls += ' input-' + this.size;
14063         }
14064         
14065         if (this.disabled) {
14066             input.disabled = true;
14067         }
14068         
14069         var inputblock = {
14070             cls : '',
14071             cn : [
14072                 input
14073             ]
14074         };
14075         
14076         if(this.before){
14077             inputblock.cls += ' input-group';
14078             
14079             inputblock.cn.unshift({
14080                 tag :'span',
14081                 cls : 'input-group-addon',
14082                 html : this.before
14083             });
14084         }
14085         
14086         if(this.removable && !this.multiple){
14087             inputblock.cls += ' roo-removable';
14088             
14089             inputblock.cn.push({
14090                 tag: 'button',
14091                 html : 'x',
14092                 cls : 'roo-combo-removable-btn close'
14093             });
14094         }
14095
14096         if(this.hasFeedback && !this.allowBlank){
14097             
14098             inputblock.cls += ' has-feedback';
14099             
14100             inputblock.cn.push({
14101                 tag: 'span',
14102                 cls: 'glyphicon form-control-feedback'
14103             });
14104             
14105         }
14106         
14107         if (this.after) {
14108             
14109             inputblock.cls += (this.before) ? '' : ' input-group';
14110             
14111             inputblock.cn.push({
14112                 tag :'span',
14113                 cls : 'input-group-addon',
14114                 html : this.after
14115             });
14116         }
14117
14118         var box = {
14119             tag: 'div',
14120             cn: [
14121                 {
14122                     tag: 'input',
14123                     type : 'hidden',
14124                     cls: 'form-hidden-field'
14125                 },
14126                 inputblock
14127             ]
14128             
14129         };
14130         
14131         if(this.multiple){
14132             box = {
14133                 tag: 'div',
14134                 cn: [
14135                     {
14136                         tag: 'input',
14137                         type : 'hidden',
14138                         cls: 'form-hidden-field'
14139                     },
14140                     {
14141                         tag: 'ul',
14142                         cls: 'roo-select2-choices',
14143                         cn:[
14144                             {
14145                                 tag: 'li',
14146                                 cls: 'roo-select2-search-field',
14147                                 cn: [
14148
14149                                     inputblock
14150                                 ]
14151                             }
14152                         ]
14153                     }
14154                 ]
14155             }
14156         };
14157         
14158         var combobox = {
14159             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14160             cn: [
14161                 box
14162             ]
14163         };
14164         
14165         if(!this.multiple && this.showToggleBtn){
14166             
14167             var caret = {
14168                         tag: 'span',
14169                         cls: 'caret'
14170             };
14171             
14172             if (this.caret != false) {
14173                 caret = {
14174                      tag: 'i',
14175                      cls: 'fa fa-' + this.caret
14176                 };
14177                 
14178             }
14179             
14180             combobox.cn.push({
14181                 tag :'span',
14182                 cls : 'input-group-addon btn dropdown-toggle',
14183                 cn : [
14184                     caret,
14185                     {
14186                         tag: 'span',
14187                         cls: 'combobox-clear',
14188                         cn  : [
14189                             {
14190                                 tag : 'i',
14191                                 cls: 'icon-remove'
14192                             }
14193                         ]
14194                     }
14195                 ]
14196
14197             })
14198         }
14199         
14200         if(this.multiple){
14201             combobox.cls += ' roo-select2-container-multi';
14202         }
14203         
14204         var align = this.labelAlign || this.parentLabelAlign();
14205         
14206         cfg.cn = combobox;
14207         
14208         if(this.fieldLabel.length && this.labelWidth){
14209             
14210             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
14211             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
14212             
14213             cfg.cn = [
14214                 {
14215                    tag : 'i',
14216                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14217                    tooltip : 'This field is required'
14218                 },
14219                 {
14220                     tag: 'label',
14221                     cls : 'control-label ' + lw,
14222                     html : this.fieldLabel
14223
14224                 },
14225                 {
14226                     cls : cw, 
14227                     cn: [
14228                         combobox
14229                     ]
14230                 }
14231             ];
14232             
14233             if(this.indicatorpos == 'right'){
14234                 cfg.cn = [
14235                     {
14236                         tag: 'label',
14237                         cls : 'control-label ' + lw,
14238                         html : this.fieldLabel
14239
14240                     },
14241                     {
14242                        tag : 'i',
14243                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14244                        tooltip : 'This field is required'
14245                     },
14246                     {
14247                         cls : cw, 
14248                         cn: [
14249                             combobox
14250                         ]
14251                     }
14252                 ];
14253             }
14254         }
14255         
14256         var settings = this;
14257         
14258         ['xs','sm','md','lg'].map(function(size){
14259             if (settings[size]) {
14260                 cfg.cls += ' col-' + size + '-' + settings[size];
14261             }
14262         });
14263         
14264         return cfg;
14265     },
14266     
14267     initTouchView : function()
14268     {
14269         this.renderTouchView();
14270         
14271         this.touchViewEl.on('scroll', function(){
14272             this.el.dom.scrollTop = 0;
14273         }, this);
14274         
14275         this.originalValue = this.getValue();
14276         
14277         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14278         
14279         this.inputEl().on("click", this.showTouchView, this);
14280         if (this.triggerEl) {
14281             this.triggerEl.on("click", this.showTouchView, this);
14282         }
14283         
14284         
14285         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14286         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14287         
14288         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14289         
14290         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14291         this.store.on('load', this.onTouchViewLoad, this);
14292         this.store.on('loadexception', this.onTouchViewLoadException, this);
14293         
14294         if(this.hiddenName){
14295             
14296             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14297             
14298             this.hiddenField.dom.value =
14299                 this.hiddenValue !== undefined ? this.hiddenValue :
14300                 this.value !== undefined ? this.value : '';
14301         
14302             this.el.dom.removeAttribute('name');
14303             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14304         }
14305         
14306         if(this.multiple){
14307             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14308             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14309         }
14310         
14311         if(this.removable && !this.multiple){
14312             var close = this.closeTriggerEl();
14313             if(close){
14314                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14315                 close.on('click', this.removeBtnClick, this, close);
14316             }
14317         }
14318         /*
14319          * fix the bug in Safari iOS8
14320          */
14321         this.inputEl().on("focus", function(e){
14322             document.activeElement.blur();
14323         }, this);
14324         
14325         return;
14326         
14327         
14328     },
14329     
14330     renderTouchView : function()
14331     {
14332         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14333         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14334         
14335         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14336         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14337         
14338         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14339         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14340         this.touchViewBodyEl.setStyle('overflow', 'auto');
14341         
14342         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14343         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14344         
14345         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14346         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14347         
14348     },
14349     
14350     showTouchView : function()
14351     {
14352         if(this.disabled){
14353             return;
14354         }
14355         
14356         this.touchViewHeaderEl.hide();
14357
14358         if(this.modalTitle.length){
14359             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14360             this.touchViewHeaderEl.show();
14361         }
14362
14363         this.touchViewEl.show();
14364
14365         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14366         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14367                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14368
14369         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14370
14371         if(this.modalTitle.length){
14372             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14373         }
14374         
14375         this.touchViewBodyEl.setHeight(bodyHeight);
14376
14377         if(this.animate){
14378             var _this = this;
14379             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14380         }else{
14381             this.touchViewEl.addClass('in');
14382         }
14383
14384         this.doTouchViewQuery();
14385         
14386     },
14387     
14388     hideTouchView : function()
14389     {
14390         this.touchViewEl.removeClass('in');
14391
14392         if(this.animate){
14393             var _this = this;
14394             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14395         }else{
14396             this.touchViewEl.setStyle('display', 'none');
14397         }
14398         
14399     },
14400     
14401     setTouchViewValue : function()
14402     {
14403         if(this.multiple){
14404             this.clearItem();
14405         
14406             var _this = this;
14407
14408             Roo.each(this.tickItems, function(o){
14409                 this.addItem(o);
14410             }, this);
14411         }
14412         
14413         this.hideTouchView();
14414     },
14415     
14416     doTouchViewQuery : function()
14417     {
14418         var qe = {
14419             query: '',
14420             forceAll: true,
14421             combo: this,
14422             cancel:false
14423         };
14424         
14425         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14426             return false;
14427         }
14428         
14429         if(!this.alwaysQuery || this.mode == 'local'){
14430             this.onTouchViewLoad();
14431             return;
14432         }
14433         
14434         this.store.load();
14435     },
14436     
14437     onTouchViewBeforeLoad : function(combo,opts)
14438     {
14439         return;
14440     },
14441
14442     // private
14443     onTouchViewLoad : function()
14444     {
14445         if(this.store.getCount() < 1){
14446             this.onTouchViewEmptyResults();
14447             return;
14448         }
14449         
14450         this.clearTouchView();
14451         
14452         var rawValue = this.getRawValue();
14453         
14454         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14455         
14456         this.tickItems = [];
14457         
14458         this.store.data.each(function(d, rowIndex){
14459             var row = this.touchViewListGroup.createChild(template);
14460             
14461             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14462                 row.addClass(d.data.cls);
14463             }
14464             
14465             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14466                 var cfg = {
14467                     data : d.data,
14468                     html : d.data[this.displayField]
14469                 };
14470                 
14471                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14472                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14473                 }
14474             }
14475             row.removeClass('selected');
14476             if(!this.multiple && this.valueField &&
14477                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14478             {
14479                 // radio buttons..
14480                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14481                 row.addClass('selected');
14482             }
14483             
14484             if(this.multiple && this.valueField &&
14485                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14486             {
14487                 
14488                 // checkboxes...
14489                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14490                 this.tickItems.push(d.data);
14491             }
14492             
14493             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14494             
14495         }, this);
14496         
14497         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14498         
14499         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14500
14501         if(this.modalTitle.length){
14502             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14503         }
14504
14505         var listHeight = this.touchViewListGroup.getHeight();
14506         
14507         var _this = this;
14508         
14509         if(firstChecked && listHeight > bodyHeight){
14510             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14511         }
14512         
14513     },
14514     
14515     onTouchViewLoadException : function()
14516     {
14517         this.hideTouchView();
14518     },
14519     
14520     onTouchViewEmptyResults : function()
14521     {
14522         this.clearTouchView();
14523         
14524         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14525         
14526         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14527         
14528     },
14529     
14530     clearTouchView : function()
14531     {
14532         this.touchViewListGroup.dom.innerHTML = '';
14533     },
14534     
14535     onTouchViewClick : function(e, el, o)
14536     {
14537         e.preventDefault();
14538         
14539         var row = o.row;
14540         var rowIndex = o.rowIndex;
14541         
14542         var r = this.store.getAt(rowIndex);
14543         
14544         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14545             
14546             if(!this.multiple){
14547                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14548                     c.dom.removeAttribute('checked');
14549                 }, this);
14550
14551                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14552
14553                 this.setFromData(r.data);
14554
14555                 var close = this.closeTriggerEl();
14556
14557                 if(close){
14558                     close.show();
14559                 }
14560
14561                 this.hideTouchView();
14562
14563                 this.fireEvent('select', this, r, rowIndex);
14564
14565                 return;
14566             }
14567
14568             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14569                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14570                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14571                 return;
14572             }
14573
14574             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14575             this.addItem(r.data);
14576             this.tickItems.push(r.data);
14577         }
14578     },
14579     
14580     getAutoCreateNativeIOS : function()
14581     {
14582         var cfg = {
14583             cls: 'form-group' //input-group,
14584         };
14585         
14586         var combobox =  {
14587             tag: 'select',
14588             cls : 'roo-ios-select'
14589         };
14590         
14591         if (this.name) {
14592             combobox.name = this.name;
14593         }
14594         
14595         if (this.disabled) {
14596             combobox.disabled = true;
14597         }
14598         
14599         var settings = this;
14600         
14601         ['xs','sm','md','lg'].map(function(size){
14602             if (settings[size]) {
14603                 cfg.cls += ' col-' + size + '-' + settings[size];
14604             }
14605         });
14606         
14607         cfg.cn = combobox;
14608         
14609         return cfg;
14610         
14611     },
14612     
14613     initIOSView : function()
14614     {
14615         this.store.on('load', this.onIOSViewLoad, this);
14616         
14617         return;
14618     },
14619     
14620     onIOSViewLoad : function()
14621     {
14622         if(this.store.getCount() < 1){
14623             return;
14624         }
14625         
14626         this.clearIOSView();
14627         
14628         if(this.allowBlank) {
14629             
14630             var default_text = '-- SELECT --';
14631             
14632             var opt = this.inputEl().createChild({
14633                 tag: 'option',
14634                 value : 0,
14635                 html : default_text
14636             });
14637             
14638             var o = {};
14639             o[this.valueField] = 0;
14640             o[this.displayField] = default_text;
14641             
14642             this.ios_options.push({
14643                 data : o,
14644                 el : opt
14645             });
14646             
14647         }
14648         
14649         this.store.data.each(function(d, rowIndex){
14650             
14651             var html = '';
14652             
14653             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14654                 html = d.data[this.displayField];
14655             }
14656             
14657             var value = '';
14658             
14659             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
14660                 value = d.data[this.valueField];
14661             }
14662             
14663             var option = {
14664                 tag: 'option',
14665                 value : value,
14666                 html : html
14667             };
14668             
14669             if(this.value == d.data[this.valueField]){
14670                 option['selected'] = true;
14671             }
14672             
14673             var opt = this.inputEl().createChild(option);
14674             
14675             this.ios_options.push({
14676                 data : d.data,
14677                 el : opt
14678             });
14679             
14680         }, this);
14681         
14682         this.inputEl().on('change', function(){
14683            this.fireEvent('select', this);
14684         }, this);
14685         
14686     },
14687     
14688     clearIOSView: function()
14689     {
14690         this.inputEl().dom.innerHTML = '';
14691         
14692         this.ios_options = [];
14693     },
14694     
14695     setIOSValue: function(v)
14696     {
14697         this.value = v;
14698         
14699         if(!this.ios_options){
14700             return;
14701         }
14702         
14703         Roo.each(this.ios_options, function(opts){
14704            
14705            opts.el.dom.removeAttribute('selected');
14706            
14707            if(opts.data[this.valueField] != v){
14708                return;
14709            }
14710            
14711            opts.el.dom.setAttribute('selected', true);
14712            
14713         }, this);
14714     }
14715
14716     /** 
14717     * @cfg {Boolean} grow 
14718     * @hide 
14719     */
14720     /** 
14721     * @cfg {Number} growMin 
14722     * @hide 
14723     */
14724     /** 
14725     * @cfg {Number} growMax 
14726     * @hide 
14727     */
14728     /**
14729      * @hide
14730      * @method autoSize
14731      */
14732 });
14733
14734 Roo.apply(Roo.bootstrap.ComboBox,  {
14735     
14736     header : {
14737         tag: 'div',
14738         cls: 'modal-header',
14739         cn: [
14740             {
14741                 tag: 'h4',
14742                 cls: 'modal-title'
14743             }
14744         ]
14745     },
14746     
14747     body : {
14748         tag: 'div',
14749         cls: 'modal-body',
14750         cn: [
14751             {
14752                 tag: 'ul',
14753                 cls: 'list-group'
14754             }
14755         ]
14756     },
14757     
14758     listItemRadio : {
14759         tag: 'li',
14760         cls: 'list-group-item',
14761         cn: [
14762             {
14763                 tag: 'span',
14764                 cls: 'roo-combobox-list-group-item-value'
14765             },
14766             {
14767                 tag: 'div',
14768                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14769                 cn: [
14770                     {
14771                         tag: 'input',
14772                         type: 'radio'
14773                     },
14774                     {
14775                         tag: 'label'
14776                     }
14777                 ]
14778             }
14779         ]
14780     },
14781     
14782     listItemCheckbox : {
14783         tag: 'li',
14784         cls: 'list-group-item',
14785         cn: [
14786             {
14787                 tag: 'span',
14788                 cls: 'roo-combobox-list-group-item-value'
14789             },
14790             {
14791                 tag: 'div',
14792                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14793                 cn: [
14794                     {
14795                         tag: 'input',
14796                         type: 'checkbox'
14797                     },
14798                     {
14799                         tag: 'label'
14800                     }
14801                 ]
14802             }
14803         ]
14804     },
14805     
14806     emptyResult : {
14807         tag: 'div',
14808         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14809     },
14810     
14811     footer : {
14812         tag: 'div',
14813         cls: 'modal-footer',
14814         cn: [
14815             {
14816                 tag: 'div',
14817                 cls: 'row',
14818                 cn: [
14819                     {
14820                         tag: 'div',
14821                         cls: 'col-xs-6 text-left',
14822                         cn: {
14823                             tag: 'button',
14824                             cls: 'btn btn-danger roo-touch-view-cancel',
14825                             html: 'Cancel'
14826                         }
14827                     },
14828                     {
14829                         tag: 'div',
14830                         cls: 'col-xs-6 text-right',
14831                         cn: {
14832                             tag: 'button',
14833                             cls: 'btn btn-success roo-touch-view-ok',
14834                             html: 'OK'
14835                         }
14836                     }
14837                 ]
14838             }
14839         ]
14840         
14841     }
14842 });
14843
14844 Roo.apply(Roo.bootstrap.ComboBox,  {
14845     
14846     touchViewTemplate : {
14847         tag: 'div',
14848         cls: 'modal fade roo-combobox-touch-view',
14849         cn: [
14850             {
14851                 tag: 'div',
14852                 cls: 'modal-dialog',
14853                 style : 'position:fixed', // we have to fix position....
14854                 cn: [
14855                     {
14856                         tag: 'div',
14857                         cls: 'modal-content',
14858                         cn: [
14859                             Roo.bootstrap.ComboBox.header,
14860                             Roo.bootstrap.ComboBox.body,
14861                             Roo.bootstrap.ComboBox.footer
14862                         ]
14863                     }
14864                 ]
14865             }
14866         ]
14867     }
14868 });/*
14869  * Based on:
14870  * Ext JS Library 1.1.1
14871  * Copyright(c) 2006-2007, Ext JS, LLC.
14872  *
14873  * Originally Released Under LGPL - original licence link has changed is not relivant.
14874  *
14875  * Fork - LGPL
14876  * <script type="text/javascript">
14877  */
14878
14879 /**
14880  * @class Roo.View
14881  * @extends Roo.util.Observable
14882  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14883  * This class also supports single and multi selection modes. <br>
14884  * Create a data model bound view:
14885  <pre><code>
14886  var store = new Roo.data.Store(...);
14887
14888  var view = new Roo.View({
14889     el : "my-element",
14890     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14891  
14892     singleSelect: true,
14893     selectedClass: "ydataview-selected",
14894     store: store
14895  });
14896
14897  // listen for node click?
14898  view.on("click", function(vw, index, node, e){
14899  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14900  });
14901
14902  // load XML data
14903  dataModel.load("foobar.xml");
14904  </code></pre>
14905  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14906  * <br><br>
14907  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14908  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14909  * 
14910  * Note: old style constructor is still suported (container, template, config)
14911  * 
14912  * @constructor
14913  * Create a new View
14914  * @param {Object} config The config object
14915  * 
14916  */
14917 Roo.View = function(config, depreciated_tpl, depreciated_config){
14918     
14919     this.parent = false;
14920     
14921     if (typeof(depreciated_tpl) == 'undefined') {
14922         // new way.. - universal constructor.
14923         Roo.apply(this, config);
14924         this.el  = Roo.get(this.el);
14925     } else {
14926         // old format..
14927         this.el  = Roo.get(config);
14928         this.tpl = depreciated_tpl;
14929         Roo.apply(this, depreciated_config);
14930     }
14931     this.wrapEl  = this.el.wrap().wrap();
14932     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14933     
14934     
14935     if(typeof(this.tpl) == "string"){
14936         this.tpl = new Roo.Template(this.tpl);
14937     } else {
14938         // support xtype ctors..
14939         this.tpl = new Roo.factory(this.tpl, Roo);
14940     }
14941     
14942     
14943     this.tpl.compile();
14944     
14945     /** @private */
14946     this.addEvents({
14947         /**
14948          * @event beforeclick
14949          * Fires before a click is processed. Returns false to cancel the default action.
14950          * @param {Roo.View} this
14951          * @param {Number} index The index of the target node
14952          * @param {HTMLElement} node The target node
14953          * @param {Roo.EventObject} e The raw event object
14954          */
14955             "beforeclick" : true,
14956         /**
14957          * @event click
14958          * Fires when a template node is clicked.
14959          * @param {Roo.View} this
14960          * @param {Number} index The index of the target node
14961          * @param {HTMLElement} node The target node
14962          * @param {Roo.EventObject} e The raw event object
14963          */
14964             "click" : true,
14965         /**
14966          * @event dblclick
14967          * Fires when a template node is double clicked.
14968          * @param {Roo.View} this
14969          * @param {Number} index The index of the target node
14970          * @param {HTMLElement} node The target node
14971          * @param {Roo.EventObject} e The raw event object
14972          */
14973             "dblclick" : true,
14974         /**
14975          * @event contextmenu
14976          * Fires when a template node is right clicked.
14977          * @param {Roo.View} this
14978          * @param {Number} index The index of the target node
14979          * @param {HTMLElement} node The target node
14980          * @param {Roo.EventObject} e The raw event object
14981          */
14982             "contextmenu" : true,
14983         /**
14984          * @event selectionchange
14985          * Fires when the selected nodes change.
14986          * @param {Roo.View} this
14987          * @param {Array} selections Array of the selected nodes
14988          */
14989             "selectionchange" : true,
14990     
14991         /**
14992          * @event beforeselect
14993          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14994          * @param {Roo.View} this
14995          * @param {HTMLElement} node The node to be selected
14996          * @param {Array} selections Array of currently selected nodes
14997          */
14998             "beforeselect" : true,
14999         /**
15000          * @event preparedata
15001          * Fires on every row to render, to allow you to change the data.
15002          * @param {Roo.View} this
15003          * @param {Object} data to be rendered (change this)
15004          */
15005           "preparedata" : true
15006           
15007           
15008         });
15009
15010
15011
15012     this.el.on({
15013         "click": this.onClick,
15014         "dblclick": this.onDblClick,
15015         "contextmenu": this.onContextMenu,
15016         scope:this
15017     });
15018
15019     this.selections = [];
15020     this.nodes = [];
15021     this.cmp = new Roo.CompositeElementLite([]);
15022     if(this.store){
15023         this.store = Roo.factory(this.store, Roo.data);
15024         this.setStore(this.store, true);
15025     }
15026     
15027     if ( this.footer && this.footer.xtype) {
15028            
15029          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15030         
15031         this.footer.dataSource = this.store;
15032         this.footer.container = fctr;
15033         this.footer = Roo.factory(this.footer, Roo);
15034         fctr.insertFirst(this.el);
15035         
15036         // this is a bit insane - as the paging toolbar seems to detach the el..
15037 //        dom.parentNode.parentNode.parentNode
15038          // they get detached?
15039     }
15040     
15041     
15042     Roo.View.superclass.constructor.call(this);
15043     
15044     
15045 };
15046
15047 Roo.extend(Roo.View, Roo.util.Observable, {
15048     
15049      /**
15050      * @cfg {Roo.data.Store} store Data store to load data from.
15051      */
15052     store : false,
15053     
15054     /**
15055      * @cfg {String|Roo.Element} el The container element.
15056      */
15057     el : '',
15058     
15059     /**
15060      * @cfg {String|Roo.Template} tpl The template used by this View 
15061      */
15062     tpl : false,
15063     /**
15064      * @cfg {String} dataName the named area of the template to use as the data area
15065      *                          Works with domtemplates roo-name="name"
15066      */
15067     dataName: false,
15068     /**
15069      * @cfg {String} selectedClass The css class to add to selected nodes
15070      */
15071     selectedClass : "x-view-selected",
15072      /**
15073      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15074      */
15075     emptyText : "",
15076     
15077     /**
15078      * @cfg {String} text to display on mask (default Loading)
15079      */
15080     mask : false,
15081     /**
15082      * @cfg {Boolean} multiSelect Allow multiple selection
15083      */
15084     multiSelect : false,
15085     /**
15086      * @cfg {Boolean} singleSelect Allow single selection
15087      */
15088     singleSelect:  false,
15089     
15090     /**
15091      * @cfg {Boolean} toggleSelect - selecting 
15092      */
15093     toggleSelect : false,
15094     
15095     /**
15096      * @cfg {Boolean} tickable - selecting 
15097      */
15098     tickable : false,
15099     
15100     /**
15101      * Returns the element this view is bound to.
15102      * @return {Roo.Element}
15103      */
15104     getEl : function(){
15105         return this.wrapEl;
15106     },
15107     
15108     
15109
15110     /**
15111      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15112      */
15113     refresh : function(){
15114         //Roo.log('refresh');
15115         var t = this.tpl;
15116         
15117         // if we are using something like 'domtemplate', then
15118         // the what gets used is:
15119         // t.applySubtemplate(NAME, data, wrapping data..)
15120         // the outer template then get' applied with
15121         //     the store 'extra data'
15122         // and the body get's added to the
15123         //      roo-name="data" node?
15124         //      <span class='roo-tpl-{name}'></span> ?????
15125         
15126         
15127         
15128         this.clearSelections();
15129         this.el.update("");
15130         var html = [];
15131         var records = this.store.getRange();
15132         if(records.length < 1) {
15133             
15134             // is this valid??  = should it render a template??
15135             
15136             this.el.update(this.emptyText);
15137             return;
15138         }
15139         var el = this.el;
15140         if (this.dataName) {
15141             this.el.update(t.apply(this.store.meta)); //????
15142             el = this.el.child('.roo-tpl-' + this.dataName);
15143         }
15144         
15145         for(var i = 0, len = records.length; i < len; i++){
15146             var data = this.prepareData(records[i].data, i, records[i]);
15147             this.fireEvent("preparedata", this, data, i, records[i]);
15148             
15149             var d = Roo.apply({}, data);
15150             
15151             if(this.tickable){
15152                 Roo.apply(d, {'roo-id' : Roo.id()});
15153                 
15154                 var _this = this;
15155             
15156                 Roo.each(this.parent.item, function(item){
15157                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15158                         return;
15159                     }
15160                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15161                 });
15162             }
15163             
15164             html[html.length] = Roo.util.Format.trim(
15165                 this.dataName ?
15166                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15167                     t.apply(d)
15168             );
15169         }
15170         
15171         
15172         
15173         el.update(html.join(""));
15174         this.nodes = el.dom.childNodes;
15175         this.updateIndexes(0);
15176     },
15177     
15178
15179     /**
15180      * Function to override to reformat the data that is sent to
15181      * the template for each node.
15182      * DEPRICATED - use the preparedata event handler.
15183      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15184      * a JSON object for an UpdateManager bound view).
15185      */
15186     prepareData : function(data, index, record)
15187     {
15188         this.fireEvent("preparedata", this, data, index, record);
15189         return data;
15190     },
15191
15192     onUpdate : function(ds, record){
15193         // Roo.log('on update');   
15194         this.clearSelections();
15195         var index = this.store.indexOf(record);
15196         var n = this.nodes[index];
15197         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15198         n.parentNode.removeChild(n);
15199         this.updateIndexes(index, index);
15200     },
15201
15202     
15203     
15204 // --------- FIXME     
15205     onAdd : function(ds, records, index)
15206     {
15207         //Roo.log(['on Add', ds, records, index] );        
15208         this.clearSelections();
15209         if(this.nodes.length == 0){
15210             this.refresh();
15211             return;
15212         }
15213         var n = this.nodes[index];
15214         for(var i = 0, len = records.length; i < len; i++){
15215             var d = this.prepareData(records[i].data, i, records[i]);
15216             if(n){
15217                 this.tpl.insertBefore(n, d);
15218             }else{
15219                 
15220                 this.tpl.append(this.el, d);
15221             }
15222         }
15223         this.updateIndexes(index);
15224     },
15225
15226     onRemove : function(ds, record, index){
15227        // Roo.log('onRemove');
15228         this.clearSelections();
15229         var el = this.dataName  ?
15230             this.el.child('.roo-tpl-' + this.dataName) :
15231             this.el; 
15232         
15233         el.dom.removeChild(this.nodes[index]);
15234         this.updateIndexes(index);
15235     },
15236
15237     /**
15238      * Refresh an individual node.
15239      * @param {Number} index
15240      */
15241     refreshNode : function(index){
15242         this.onUpdate(this.store, this.store.getAt(index));
15243     },
15244
15245     updateIndexes : function(startIndex, endIndex){
15246         var ns = this.nodes;
15247         startIndex = startIndex || 0;
15248         endIndex = endIndex || ns.length - 1;
15249         for(var i = startIndex; i <= endIndex; i++){
15250             ns[i].nodeIndex = i;
15251         }
15252     },
15253
15254     /**
15255      * Changes the data store this view uses and refresh the view.
15256      * @param {Store} store
15257      */
15258     setStore : function(store, initial){
15259         if(!initial && this.store){
15260             this.store.un("datachanged", this.refresh);
15261             this.store.un("add", this.onAdd);
15262             this.store.un("remove", this.onRemove);
15263             this.store.un("update", this.onUpdate);
15264             this.store.un("clear", this.refresh);
15265             this.store.un("beforeload", this.onBeforeLoad);
15266             this.store.un("load", this.onLoad);
15267             this.store.un("loadexception", this.onLoad);
15268         }
15269         if(store){
15270           
15271             store.on("datachanged", this.refresh, this);
15272             store.on("add", this.onAdd, this);
15273             store.on("remove", this.onRemove, this);
15274             store.on("update", this.onUpdate, this);
15275             store.on("clear", this.refresh, this);
15276             store.on("beforeload", this.onBeforeLoad, this);
15277             store.on("load", this.onLoad, this);
15278             store.on("loadexception", this.onLoad, this);
15279         }
15280         
15281         if(store){
15282             this.refresh();
15283         }
15284     },
15285     /**
15286      * onbeforeLoad - masks the loading area.
15287      *
15288      */
15289     onBeforeLoad : function(store,opts)
15290     {
15291          //Roo.log('onBeforeLoad');   
15292         if (!opts.add) {
15293             this.el.update("");
15294         }
15295         this.el.mask(this.mask ? this.mask : "Loading" ); 
15296     },
15297     onLoad : function ()
15298     {
15299         this.el.unmask();
15300     },
15301     
15302
15303     /**
15304      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15305      * @param {HTMLElement} node
15306      * @return {HTMLElement} The template node
15307      */
15308     findItemFromChild : function(node){
15309         var el = this.dataName  ?
15310             this.el.child('.roo-tpl-' + this.dataName,true) :
15311             this.el.dom; 
15312         
15313         if(!node || node.parentNode == el){
15314                     return node;
15315             }
15316             var p = node.parentNode;
15317             while(p && p != el){
15318             if(p.parentNode == el){
15319                 return p;
15320             }
15321             p = p.parentNode;
15322         }
15323             return null;
15324     },
15325
15326     /** @ignore */
15327     onClick : function(e){
15328         var item = this.findItemFromChild(e.getTarget());
15329         if(item){
15330             var index = this.indexOf(item);
15331             if(this.onItemClick(item, index, e) !== false){
15332                 this.fireEvent("click", this, index, item, e);
15333             }
15334         }else{
15335             this.clearSelections();
15336         }
15337     },
15338
15339     /** @ignore */
15340     onContextMenu : function(e){
15341         var item = this.findItemFromChild(e.getTarget());
15342         if(item){
15343             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15344         }
15345     },
15346
15347     /** @ignore */
15348     onDblClick : function(e){
15349         var item = this.findItemFromChild(e.getTarget());
15350         if(item){
15351             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15352         }
15353     },
15354
15355     onItemClick : function(item, index, e)
15356     {
15357         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15358             return false;
15359         }
15360         if (this.toggleSelect) {
15361             var m = this.isSelected(item) ? 'unselect' : 'select';
15362             //Roo.log(m);
15363             var _t = this;
15364             _t[m](item, true, false);
15365             return true;
15366         }
15367         if(this.multiSelect || this.singleSelect){
15368             if(this.multiSelect && e.shiftKey && this.lastSelection){
15369                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15370             }else{
15371                 this.select(item, this.multiSelect && e.ctrlKey);
15372                 this.lastSelection = item;
15373             }
15374             
15375             if(!this.tickable){
15376                 e.preventDefault();
15377             }
15378             
15379         }
15380         return true;
15381     },
15382
15383     /**
15384      * Get the number of selected nodes.
15385      * @return {Number}
15386      */
15387     getSelectionCount : function(){
15388         return this.selections.length;
15389     },
15390
15391     /**
15392      * Get the currently selected nodes.
15393      * @return {Array} An array of HTMLElements
15394      */
15395     getSelectedNodes : function(){
15396         return this.selections;
15397     },
15398
15399     /**
15400      * Get the indexes of the selected nodes.
15401      * @return {Array}
15402      */
15403     getSelectedIndexes : function(){
15404         var indexes = [], s = this.selections;
15405         for(var i = 0, len = s.length; i < len; i++){
15406             indexes.push(s[i].nodeIndex);
15407         }
15408         return indexes;
15409     },
15410
15411     /**
15412      * Clear all selections
15413      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15414      */
15415     clearSelections : function(suppressEvent){
15416         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15417             this.cmp.elements = this.selections;
15418             this.cmp.removeClass(this.selectedClass);
15419             this.selections = [];
15420             if(!suppressEvent){
15421                 this.fireEvent("selectionchange", this, this.selections);
15422             }
15423         }
15424     },
15425
15426     /**
15427      * Returns true if the passed node is selected
15428      * @param {HTMLElement/Number} node The node or node index
15429      * @return {Boolean}
15430      */
15431     isSelected : function(node){
15432         var s = this.selections;
15433         if(s.length < 1){
15434             return false;
15435         }
15436         node = this.getNode(node);
15437         return s.indexOf(node) !== -1;
15438     },
15439
15440     /**
15441      * Selects nodes.
15442      * @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
15443      * @param {Boolean} keepExisting (optional) true to keep existing selections
15444      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15445      */
15446     select : function(nodeInfo, keepExisting, suppressEvent){
15447         if(nodeInfo instanceof Array){
15448             if(!keepExisting){
15449                 this.clearSelections(true);
15450             }
15451             for(var i = 0, len = nodeInfo.length; i < len; i++){
15452                 this.select(nodeInfo[i], true, true);
15453             }
15454             return;
15455         } 
15456         var node = this.getNode(nodeInfo);
15457         if(!node || this.isSelected(node)){
15458             return; // already selected.
15459         }
15460         if(!keepExisting){
15461             this.clearSelections(true);
15462         }
15463         
15464         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15465             Roo.fly(node).addClass(this.selectedClass);
15466             this.selections.push(node);
15467             if(!suppressEvent){
15468                 this.fireEvent("selectionchange", this, this.selections);
15469             }
15470         }
15471         
15472         
15473     },
15474       /**
15475      * Unselects nodes.
15476      * @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
15477      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15478      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15479      */
15480     unselect : function(nodeInfo, keepExisting, suppressEvent)
15481     {
15482         if(nodeInfo instanceof Array){
15483             Roo.each(this.selections, function(s) {
15484                 this.unselect(s, nodeInfo);
15485             }, this);
15486             return;
15487         }
15488         var node = this.getNode(nodeInfo);
15489         if(!node || !this.isSelected(node)){
15490             //Roo.log("not selected");
15491             return; // not selected.
15492         }
15493         // fireevent???
15494         var ns = [];
15495         Roo.each(this.selections, function(s) {
15496             if (s == node ) {
15497                 Roo.fly(node).removeClass(this.selectedClass);
15498
15499                 return;
15500             }
15501             ns.push(s);
15502         },this);
15503         
15504         this.selections= ns;
15505         this.fireEvent("selectionchange", this, this.selections);
15506     },
15507
15508     /**
15509      * Gets a template node.
15510      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15511      * @return {HTMLElement} The node or null if it wasn't found
15512      */
15513     getNode : function(nodeInfo){
15514         if(typeof nodeInfo == "string"){
15515             return document.getElementById(nodeInfo);
15516         }else if(typeof nodeInfo == "number"){
15517             return this.nodes[nodeInfo];
15518         }
15519         return nodeInfo;
15520     },
15521
15522     /**
15523      * Gets a range template nodes.
15524      * @param {Number} startIndex
15525      * @param {Number} endIndex
15526      * @return {Array} An array of nodes
15527      */
15528     getNodes : function(start, end){
15529         var ns = this.nodes;
15530         start = start || 0;
15531         end = typeof end == "undefined" ? ns.length - 1 : end;
15532         var nodes = [];
15533         if(start <= end){
15534             for(var i = start; i <= end; i++){
15535                 nodes.push(ns[i]);
15536             }
15537         } else{
15538             for(var i = start; i >= end; i--){
15539                 nodes.push(ns[i]);
15540             }
15541         }
15542         return nodes;
15543     },
15544
15545     /**
15546      * Finds the index of the passed node
15547      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15548      * @return {Number} The index of the node or -1
15549      */
15550     indexOf : function(node){
15551         node = this.getNode(node);
15552         if(typeof node.nodeIndex == "number"){
15553             return node.nodeIndex;
15554         }
15555         var ns = this.nodes;
15556         for(var i = 0, len = ns.length; i < len; i++){
15557             if(ns[i] == node){
15558                 return i;
15559             }
15560         }
15561         return -1;
15562     }
15563 });
15564 /*
15565  * - LGPL
15566  *
15567  * based on jquery fullcalendar
15568  * 
15569  */
15570
15571 Roo.bootstrap = Roo.bootstrap || {};
15572 /**
15573  * @class Roo.bootstrap.Calendar
15574  * @extends Roo.bootstrap.Component
15575  * Bootstrap Calendar class
15576  * @cfg {Boolean} loadMask (true|false) default false
15577  * @cfg {Object} header generate the user specific header of the calendar, default false
15578
15579  * @constructor
15580  * Create a new Container
15581  * @param {Object} config The config object
15582  */
15583
15584
15585
15586 Roo.bootstrap.Calendar = function(config){
15587     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15588      this.addEvents({
15589         /**
15590              * @event select
15591              * Fires when a date is selected
15592              * @param {DatePicker} this
15593              * @param {Date} date The selected date
15594              */
15595         'select': true,
15596         /**
15597              * @event monthchange
15598              * Fires when the displayed month changes 
15599              * @param {DatePicker} this
15600              * @param {Date} date The selected month
15601              */
15602         'monthchange': true,
15603         /**
15604              * @event evententer
15605              * Fires when mouse over an event
15606              * @param {Calendar} this
15607              * @param {event} Event
15608              */
15609         'evententer': true,
15610         /**
15611              * @event eventleave
15612              * Fires when the mouse leaves an
15613              * @param {Calendar} this
15614              * @param {event}
15615              */
15616         'eventleave': true,
15617         /**
15618              * @event eventclick
15619              * Fires when the mouse click an
15620              * @param {Calendar} this
15621              * @param {event}
15622              */
15623         'eventclick': true
15624         
15625     });
15626
15627 };
15628
15629 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
15630     
15631      /**
15632      * @cfg {Number} startDay
15633      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15634      */
15635     startDay : 0,
15636     
15637     loadMask : false,
15638     
15639     header : false,
15640       
15641     getAutoCreate : function(){
15642         
15643         
15644         var fc_button = function(name, corner, style, content ) {
15645             return Roo.apply({},{
15646                 tag : 'span',
15647                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
15648                          (corner.length ?
15649                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15650                             ''
15651                         ),
15652                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15653                 unselectable: 'on'
15654             });
15655         };
15656         
15657         var header = {};
15658         
15659         if(!this.header){
15660             header = {
15661                 tag : 'table',
15662                 cls : 'fc-header',
15663                 style : 'width:100%',
15664                 cn : [
15665                     {
15666                         tag: 'tr',
15667                         cn : [
15668                             {
15669                                 tag : 'td',
15670                                 cls : 'fc-header-left',
15671                                 cn : [
15672                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
15673                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
15674                                     { tag: 'span', cls: 'fc-header-space' },
15675                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
15676
15677
15678                                 ]
15679                             },
15680
15681                             {
15682                                 tag : 'td',
15683                                 cls : 'fc-header-center',
15684                                 cn : [
15685                                     {
15686                                         tag: 'span',
15687                                         cls: 'fc-header-title',
15688                                         cn : {
15689                                             tag: 'H2',
15690                                             html : 'month / year'
15691                                         }
15692                                     }
15693
15694                                 ]
15695                             },
15696                             {
15697                                 tag : 'td',
15698                                 cls : 'fc-header-right',
15699                                 cn : [
15700                               /*      fc_button('month', 'left', '', 'month' ),
15701                                     fc_button('week', '', '', 'week' ),
15702                                     fc_button('day', 'right', '', 'day' )
15703                                 */    
15704
15705                                 ]
15706                             }
15707
15708                         ]
15709                     }
15710                 ]
15711             };
15712         }
15713         
15714         header = this.header;
15715         
15716        
15717         var cal_heads = function() {
15718             var ret = [];
15719             // fixme - handle this.
15720             
15721             for (var i =0; i < Date.dayNames.length; i++) {
15722                 var d = Date.dayNames[i];
15723                 ret.push({
15724                     tag: 'th',
15725                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15726                     html : d.substring(0,3)
15727                 });
15728                 
15729             }
15730             ret[0].cls += ' fc-first';
15731             ret[6].cls += ' fc-last';
15732             return ret;
15733         };
15734         var cal_cell = function(n) {
15735             return  {
15736                 tag: 'td',
15737                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15738                 cn : [
15739                     {
15740                         cn : [
15741                             {
15742                                 cls: 'fc-day-number',
15743                                 html: 'D'
15744                             },
15745                             {
15746                                 cls: 'fc-day-content',
15747                              
15748                                 cn : [
15749                                      {
15750                                         style: 'position: relative;' // height: 17px;
15751                                     }
15752                                 ]
15753                             }
15754                             
15755                             
15756                         ]
15757                     }
15758                 ]
15759                 
15760             }
15761         };
15762         var cal_rows = function() {
15763             
15764             var ret = [];
15765             for (var r = 0; r < 6; r++) {
15766                 var row= {
15767                     tag : 'tr',
15768                     cls : 'fc-week',
15769                     cn : []
15770                 };
15771                 
15772                 for (var i =0; i < Date.dayNames.length; i++) {
15773                     var d = Date.dayNames[i];
15774                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15775
15776                 }
15777                 row.cn[0].cls+=' fc-first';
15778                 row.cn[0].cn[0].style = 'min-height:90px';
15779                 row.cn[6].cls+=' fc-last';
15780                 ret.push(row);
15781                 
15782             }
15783             ret[0].cls += ' fc-first';
15784             ret[4].cls += ' fc-prev-last';
15785             ret[5].cls += ' fc-last';
15786             return ret;
15787             
15788         };
15789         
15790         var cal_table = {
15791             tag: 'table',
15792             cls: 'fc-border-separate',
15793             style : 'width:100%',
15794             cellspacing  : 0,
15795             cn : [
15796                 { 
15797                     tag: 'thead',
15798                     cn : [
15799                         { 
15800                             tag: 'tr',
15801                             cls : 'fc-first fc-last',
15802                             cn : cal_heads()
15803                         }
15804                     ]
15805                 },
15806                 { 
15807                     tag: 'tbody',
15808                     cn : cal_rows()
15809                 }
15810                   
15811             ]
15812         };
15813          
15814          var cfg = {
15815             cls : 'fc fc-ltr',
15816             cn : [
15817                 header,
15818                 {
15819                     cls : 'fc-content',
15820                     style : "position: relative;",
15821                     cn : [
15822                         {
15823                             cls : 'fc-view fc-view-month fc-grid',
15824                             style : 'position: relative',
15825                             unselectable : 'on',
15826                             cn : [
15827                                 {
15828                                     cls : 'fc-event-container',
15829                                     style : 'position:absolute;z-index:8;top:0;left:0;'
15830                                 },
15831                                 cal_table
15832                             ]
15833                         }
15834                     ]
15835     
15836                 }
15837            ] 
15838             
15839         };
15840         
15841          
15842         
15843         return cfg;
15844     },
15845     
15846     
15847     initEvents : function()
15848     {
15849         if(!this.store){
15850             throw "can not find store for calendar";
15851         }
15852         
15853         var mark = {
15854             tag: "div",
15855             cls:"x-dlg-mask",
15856             style: "text-align:center",
15857             cn: [
15858                 {
15859                     tag: "div",
15860                     style: "background-color:white;width:50%;margin:250 auto",
15861                     cn: [
15862                         {
15863                             tag: "img",
15864                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15865                         },
15866                         {
15867                             tag: "span",
15868                             html: "Loading"
15869                         }
15870                         
15871                     ]
15872                 }
15873             ]
15874         };
15875         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15876         
15877         var size = this.el.select('.fc-content', true).first().getSize();
15878         this.maskEl.setSize(size.width, size.height);
15879         this.maskEl.enableDisplayMode("block");
15880         if(!this.loadMask){
15881             this.maskEl.hide();
15882         }
15883         
15884         this.store = Roo.factory(this.store, Roo.data);
15885         this.store.on('load', this.onLoad, this);
15886         this.store.on('beforeload', this.onBeforeLoad, this);
15887         
15888         this.resize();
15889         
15890         this.cells = this.el.select('.fc-day',true);
15891         //Roo.log(this.cells);
15892         this.textNodes = this.el.query('.fc-day-number');
15893         this.cells.addClassOnOver('fc-state-hover');
15894         
15895         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15896         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15897         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15898         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15899         
15900         this.on('monthchange', this.onMonthChange, this);
15901         
15902         this.update(new Date().clearTime());
15903     },
15904     
15905     resize : function() {
15906         var sz  = this.el.getSize();
15907         
15908         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15909         this.el.select('.fc-day-content div',true).setHeight(34);
15910     },
15911     
15912     
15913     // private
15914     showPrevMonth : function(e){
15915         this.update(this.activeDate.add("mo", -1));
15916     },
15917     showToday : function(e){
15918         this.update(new Date().clearTime());
15919     },
15920     // private
15921     showNextMonth : function(e){
15922         this.update(this.activeDate.add("mo", 1));
15923     },
15924
15925     // private
15926     showPrevYear : function(){
15927         this.update(this.activeDate.add("y", -1));
15928     },
15929
15930     // private
15931     showNextYear : function(){
15932         this.update(this.activeDate.add("y", 1));
15933     },
15934
15935     
15936    // private
15937     update : function(date)
15938     {
15939         var vd = this.activeDate;
15940         this.activeDate = date;
15941 //        if(vd && this.el){
15942 //            var t = date.getTime();
15943 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15944 //                Roo.log('using add remove');
15945 //                
15946 //                this.fireEvent('monthchange', this, date);
15947 //                
15948 //                this.cells.removeClass("fc-state-highlight");
15949 //                this.cells.each(function(c){
15950 //                   if(c.dateValue == t){
15951 //                       c.addClass("fc-state-highlight");
15952 //                       setTimeout(function(){
15953 //                            try{c.dom.firstChild.focus();}catch(e){}
15954 //                       }, 50);
15955 //                       return false;
15956 //                   }
15957 //                   return true;
15958 //                });
15959 //                return;
15960 //            }
15961 //        }
15962         
15963         var days = date.getDaysInMonth();
15964         
15965         var firstOfMonth = date.getFirstDateOfMonth();
15966         var startingPos = firstOfMonth.getDay()-this.startDay;
15967         
15968         if(startingPos < this.startDay){
15969             startingPos += 7;
15970         }
15971         
15972         var pm = date.add(Date.MONTH, -1);
15973         var prevStart = pm.getDaysInMonth()-startingPos;
15974 //        
15975         this.cells = this.el.select('.fc-day',true);
15976         this.textNodes = this.el.query('.fc-day-number');
15977         this.cells.addClassOnOver('fc-state-hover');
15978         
15979         var cells = this.cells.elements;
15980         var textEls = this.textNodes;
15981         
15982         Roo.each(cells, function(cell){
15983             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15984         });
15985         
15986         days += startingPos;
15987
15988         // convert everything to numbers so it's fast
15989         var day = 86400000;
15990         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15991         //Roo.log(d);
15992         //Roo.log(pm);
15993         //Roo.log(prevStart);
15994         
15995         var today = new Date().clearTime().getTime();
15996         var sel = date.clearTime().getTime();
15997         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15998         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15999         var ddMatch = this.disabledDatesRE;
16000         var ddText = this.disabledDatesText;
16001         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16002         var ddaysText = this.disabledDaysText;
16003         var format = this.format;
16004         
16005         var setCellClass = function(cal, cell){
16006             cell.row = 0;
16007             cell.events = [];
16008             cell.more = [];
16009             //Roo.log('set Cell Class');
16010             cell.title = "";
16011             var t = d.getTime();
16012             
16013             //Roo.log(d);
16014             
16015             cell.dateValue = t;
16016             if(t == today){
16017                 cell.className += " fc-today";
16018                 cell.className += " fc-state-highlight";
16019                 cell.title = cal.todayText;
16020             }
16021             if(t == sel){
16022                 // disable highlight in other month..
16023                 //cell.className += " fc-state-highlight";
16024                 
16025             }
16026             // disabling
16027             if(t < min) {
16028                 cell.className = " fc-state-disabled";
16029                 cell.title = cal.minText;
16030                 return;
16031             }
16032             if(t > max) {
16033                 cell.className = " fc-state-disabled";
16034                 cell.title = cal.maxText;
16035                 return;
16036             }
16037             if(ddays){
16038                 if(ddays.indexOf(d.getDay()) != -1){
16039                     cell.title = ddaysText;
16040                     cell.className = " fc-state-disabled";
16041                 }
16042             }
16043             if(ddMatch && format){
16044                 var fvalue = d.dateFormat(format);
16045                 if(ddMatch.test(fvalue)){
16046                     cell.title = ddText.replace("%0", fvalue);
16047                     cell.className = " fc-state-disabled";
16048                 }
16049             }
16050             
16051             if (!cell.initialClassName) {
16052                 cell.initialClassName = cell.dom.className;
16053             }
16054             
16055             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16056         };
16057
16058         var i = 0;
16059         
16060         for(; i < startingPos; i++) {
16061             textEls[i].innerHTML = (++prevStart);
16062             d.setDate(d.getDate()+1);
16063             
16064             cells[i].className = "fc-past fc-other-month";
16065             setCellClass(this, cells[i]);
16066         }
16067         
16068         var intDay = 0;
16069         
16070         for(; i < days; i++){
16071             intDay = i - startingPos + 1;
16072             textEls[i].innerHTML = (intDay);
16073             d.setDate(d.getDate()+1);
16074             
16075             cells[i].className = ''; // "x-date-active";
16076             setCellClass(this, cells[i]);
16077         }
16078         var extraDays = 0;
16079         
16080         for(; i < 42; i++) {
16081             textEls[i].innerHTML = (++extraDays);
16082             d.setDate(d.getDate()+1);
16083             
16084             cells[i].className = "fc-future fc-other-month";
16085             setCellClass(this, cells[i]);
16086         }
16087         
16088         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16089         
16090         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16091         
16092         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16093         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16094         
16095         if(totalRows != 6){
16096             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16097             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16098         }
16099         
16100         this.fireEvent('monthchange', this, date);
16101         
16102         
16103         /*
16104         if(!this.internalRender){
16105             var main = this.el.dom.firstChild;
16106             var w = main.offsetWidth;
16107             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16108             Roo.fly(main).setWidth(w);
16109             this.internalRender = true;
16110             // opera does not respect the auto grow header center column
16111             // then, after it gets a width opera refuses to recalculate
16112             // without a second pass
16113             if(Roo.isOpera && !this.secondPass){
16114                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16115                 this.secondPass = true;
16116                 this.update.defer(10, this, [date]);
16117             }
16118         }
16119         */
16120         
16121     },
16122     
16123     findCell : function(dt) {
16124         dt = dt.clearTime().getTime();
16125         var ret = false;
16126         this.cells.each(function(c){
16127             //Roo.log("check " +c.dateValue + '?=' + dt);
16128             if(c.dateValue == dt){
16129                 ret = c;
16130                 return false;
16131             }
16132             return true;
16133         });
16134         
16135         return ret;
16136     },
16137     
16138     findCells : function(ev) {
16139         var s = ev.start.clone().clearTime().getTime();
16140        // Roo.log(s);
16141         var e= ev.end.clone().clearTime().getTime();
16142        // Roo.log(e);
16143         var ret = [];
16144         this.cells.each(function(c){
16145              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16146             
16147             if(c.dateValue > e){
16148                 return ;
16149             }
16150             if(c.dateValue < s){
16151                 return ;
16152             }
16153             ret.push(c);
16154         });
16155         
16156         return ret;    
16157     },
16158     
16159 //    findBestRow: function(cells)
16160 //    {
16161 //        var ret = 0;
16162 //        
16163 //        for (var i =0 ; i < cells.length;i++) {
16164 //            ret  = Math.max(cells[i].rows || 0,ret);
16165 //        }
16166 //        return ret;
16167 //        
16168 //    },
16169     
16170     
16171     addItem : function(ev)
16172     {
16173         // look for vertical location slot in
16174         var cells = this.findCells(ev);
16175         
16176 //        ev.row = this.findBestRow(cells);
16177         
16178         // work out the location.
16179         
16180         var crow = false;
16181         var rows = [];
16182         for(var i =0; i < cells.length; i++) {
16183             
16184             cells[i].row = cells[0].row;
16185             
16186             if(i == 0){
16187                 cells[i].row = cells[i].row + 1;
16188             }
16189             
16190             if (!crow) {
16191                 crow = {
16192                     start : cells[i],
16193                     end :  cells[i]
16194                 };
16195                 continue;
16196             }
16197             if (crow.start.getY() == cells[i].getY()) {
16198                 // on same row.
16199                 crow.end = cells[i];
16200                 continue;
16201             }
16202             // different row.
16203             rows.push(crow);
16204             crow = {
16205                 start: cells[i],
16206                 end : cells[i]
16207             };
16208             
16209         }
16210         
16211         rows.push(crow);
16212         ev.els = [];
16213         ev.rows = rows;
16214         ev.cells = cells;
16215         
16216         cells[0].events.push(ev);
16217         
16218         this.calevents.push(ev);
16219     },
16220     
16221     clearEvents: function() {
16222         
16223         if(!this.calevents){
16224             return;
16225         }
16226         
16227         Roo.each(this.cells.elements, function(c){
16228             c.row = 0;
16229             c.events = [];
16230             c.more = [];
16231         });
16232         
16233         Roo.each(this.calevents, function(e) {
16234             Roo.each(e.els, function(el) {
16235                 el.un('mouseenter' ,this.onEventEnter, this);
16236                 el.un('mouseleave' ,this.onEventLeave, this);
16237                 el.remove();
16238             },this);
16239         },this);
16240         
16241         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16242             e.remove();
16243         });
16244         
16245     },
16246     
16247     renderEvents: function()
16248     {   
16249         var _this = this;
16250         
16251         this.cells.each(function(c) {
16252             
16253             if(c.row < 5){
16254                 return;
16255             }
16256             
16257             var ev = c.events;
16258             
16259             var r = 4;
16260             if(c.row != c.events.length){
16261                 r = 4 - (4 - (c.row - c.events.length));
16262             }
16263             
16264             c.events = ev.slice(0, r);
16265             c.more = ev.slice(r);
16266             
16267             if(c.more.length && c.more.length == 1){
16268                 c.events.push(c.more.pop());
16269             }
16270             
16271             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16272             
16273         });
16274             
16275         this.cells.each(function(c) {
16276             
16277             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16278             
16279             
16280             for (var e = 0; e < c.events.length; e++){
16281                 var ev = c.events[e];
16282                 var rows = ev.rows;
16283                 
16284                 for(var i = 0; i < rows.length; i++) {
16285                 
16286                     // how many rows should it span..
16287
16288                     var  cfg = {
16289                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16290                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16291
16292                         unselectable : "on",
16293                         cn : [
16294                             {
16295                                 cls: 'fc-event-inner',
16296                                 cn : [
16297     //                                {
16298     //                                  tag:'span',
16299     //                                  cls: 'fc-event-time',
16300     //                                  html : cells.length > 1 ? '' : ev.time
16301     //                                },
16302                                     {
16303                                       tag:'span',
16304                                       cls: 'fc-event-title',
16305                                       html : String.format('{0}', ev.title)
16306                                     }
16307
16308
16309                                 ]
16310                             },
16311                             {
16312                                 cls: 'ui-resizable-handle ui-resizable-e',
16313                                 html : '&nbsp;&nbsp;&nbsp'
16314                             }
16315
16316                         ]
16317                     };
16318
16319                     if (i == 0) {
16320                         cfg.cls += ' fc-event-start';
16321                     }
16322                     if ((i+1) == rows.length) {
16323                         cfg.cls += ' fc-event-end';
16324                     }
16325
16326                     var ctr = _this.el.select('.fc-event-container',true).first();
16327                     var cg = ctr.createChild(cfg);
16328
16329                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16330                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16331
16332                     var r = (c.more.length) ? 1 : 0;
16333                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16334                     cg.setWidth(ebox.right - sbox.x -2);
16335
16336                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16337                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16338                     cg.on('click', _this.onEventClick, _this, ev);
16339
16340                     ev.els.push(cg);
16341                     
16342                 }
16343                 
16344             }
16345             
16346             
16347             if(c.more.length){
16348                 var  cfg = {
16349                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16350                     style : 'position: absolute',
16351                     unselectable : "on",
16352                     cn : [
16353                         {
16354                             cls: 'fc-event-inner',
16355                             cn : [
16356                                 {
16357                                   tag:'span',
16358                                   cls: 'fc-event-title',
16359                                   html : 'More'
16360                                 }
16361
16362
16363                             ]
16364                         },
16365                         {
16366                             cls: 'ui-resizable-handle ui-resizable-e',
16367                             html : '&nbsp;&nbsp;&nbsp'
16368                         }
16369
16370                     ]
16371                 };
16372
16373                 var ctr = _this.el.select('.fc-event-container',true).first();
16374                 var cg = ctr.createChild(cfg);
16375
16376                 var sbox = c.select('.fc-day-content',true).first().getBox();
16377                 var ebox = c.select('.fc-day-content',true).first().getBox();
16378                 //Roo.log(cg);
16379                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16380                 cg.setWidth(ebox.right - sbox.x -2);
16381
16382                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16383                 
16384             }
16385             
16386         });
16387         
16388         
16389         
16390     },
16391     
16392     onEventEnter: function (e, el,event,d) {
16393         this.fireEvent('evententer', this, el, event);
16394     },
16395     
16396     onEventLeave: function (e, el,event,d) {
16397         this.fireEvent('eventleave', this, el, event);
16398     },
16399     
16400     onEventClick: function (e, el,event,d) {
16401         this.fireEvent('eventclick', this, el, event);
16402     },
16403     
16404     onMonthChange: function () {
16405         this.store.load();
16406     },
16407     
16408     onMoreEventClick: function(e, el, more)
16409     {
16410         var _this = this;
16411         
16412         this.calpopover.placement = 'right';
16413         this.calpopover.setTitle('More');
16414         
16415         this.calpopover.setContent('');
16416         
16417         var ctr = this.calpopover.el.select('.popover-content', true).first();
16418         
16419         Roo.each(more, function(m){
16420             var cfg = {
16421                 cls : 'fc-event-hori fc-event-draggable',
16422                 html : m.title
16423             };
16424             var cg = ctr.createChild(cfg);
16425             
16426             cg.on('click', _this.onEventClick, _this, m);
16427         });
16428         
16429         this.calpopover.show(el);
16430         
16431         
16432     },
16433     
16434     onLoad: function () 
16435     {   
16436         this.calevents = [];
16437         var cal = this;
16438         
16439         if(this.store.getCount() > 0){
16440             this.store.data.each(function(d){
16441                cal.addItem({
16442                     id : d.data.id,
16443                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16444                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16445                     time : d.data.start_time,
16446                     title : d.data.title,
16447                     description : d.data.description,
16448                     venue : d.data.venue
16449                 });
16450             });
16451         }
16452         
16453         this.renderEvents();
16454         
16455         if(this.calevents.length && this.loadMask){
16456             this.maskEl.hide();
16457         }
16458     },
16459     
16460     onBeforeLoad: function()
16461     {
16462         this.clearEvents();
16463         if(this.loadMask){
16464             this.maskEl.show();
16465         }
16466     }
16467 });
16468
16469  
16470  /*
16471  * - LGPL
16472  *
16473  * element
16474  * 
16475  */
16476
16477 /**
16478  * @class Roo.bootstrap.Popover
16479  * @extends Roo.bootstrap.Component
16480  * Bootstrap Popover class
16481  * @cfg {String} html contents of the popover   (or false to use children..)
16482  * @cfg {String} title of popover (or false to hide)
16483  * @cfg {String} placement how it is placed
16484  * @cfg {String} trigger click || hover (or false to trigger manually)
16485  * @cfg {String} over what (parent or false to trigger manually.)
16486  * @cfg {Number} delay - delay before showing
16487  
16488  * @constructor
16489  * Create a new Popover
16490  * @param {Object} config The config object
16491  */
16492
16493 Roo.bootstrap.Popover = function(config){
16494     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16495     
16496     this.addEvents({
16497         // raw events
16498          /**
16499          * @event show
16500          * After the popover show
16501          * 
16502          * @param {Roo.bootstrap.Popover} this
16503          */
16504         "show" : true,
16505         /**
16506          * @event hide
16507          * After the popover hide
16508          * 
16509          * @param {Roo.bootstrap.Popover} this
16510          */
16511         "hide" : true
16512     });
16513 };
16514
16515 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
16516     
16517     title: 'Fill in a title',
16518     html: false,
16519     
16520     placement : 'right',
16521     trigger : 'hover', // hover
16522     
16523     delay : 0,
16524     
16525     over: 'parent',
16526     
16527     can_build_overlaid : false,
16528     
16529     getChildContainer : function()
16530     {
16531         return this.el.select('.popover-content',true).first();
16532     },
16533     
16534     getAutoCreate : function(){
16535          
16536         var cfg = {
16537            cls : 'popover roo-dynamic',
16538            style: 'display:block',
16539            cn : [
16540                 {
16541                     cls : 'arrow'
16542                 },
16543                 {
16544                     cls : 'popover-inner',
16545                     cn : [
16546                         {
16547                             tag: 'h3',
16548                             cls: 'popover-title',
16549                             html : this.title
16550                         },
16551                         {
16552                             cls : 'popover-content',
16553                             html : this.html
16554                         }
16555                     ]
16556                     
16557                 }
16558            ]
16559         };
16560         
16561         return cfg;
16562     },
16563     setTitle: function(str)
16564     {
16565         this.title = str;
16566         this.el.select('.popover-title',true).first().dom.innerHTML = str;
16567     },
16568     setContent: function(str)
16569     {
16570         this.html = str;
16571         this.el.select('.popover-content',true).first().dom.innerHTML = str;
16572     },
16573     // as it get's added to the bottom of the page.
16574     onRender : function(ct, position)
16575     {
16576         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16577         if(!this.el){
16578             var cfg = Roo.apply({},  this.getAutoCreate());
16579             cfg.id = Roo.id();
16580             
16581             if (this.cls) {
16582                 cfg.cls += ' ' + this.cls;
16583             }
16584             if (this.style) {
16585                 cfg.style = this.style;
16586             }
16587             //Roo.log("adding to ");
16588             this.el = Roo.get(document.body).createChild(cfg, position);
16589 //            Roo.log(this.el);
16590         }
16591         this.initEvents();
16592     },
16593     
16594     initEvents : function()
16595     {
16596         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16597         this.el.enableDisplayMode('block');
16598         this.el.hide();
16599         if (this.over === false) {
16600             return; 
16601         }
16602         if (this.triggers === false) {
16603             return;
16604         }
16605         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16606         var triggers = this.trigger ? this.trigger.split(' ') : [];
16607         Roo.each(triggers, function(trigger) {
16608         
16609             if (trigger == 'click') {
16610                 on_el.on('click', this.toggle, this);
16611             } else if (trigger != 'manual') {
16612                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
16613                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16614       
16615                 on_el.on(eventIn  ,this.enter, this);
16616                 on_el.on(eventOut, this.leave, this);
16617             }
16618         }, this);
16619         
16620     },
16621     
16622     
16623     // private
16624     timeout : null,
16625     hoverState : null,
16626     
16627     toggle : function () {
16628         this.hoverState == 'in' ? this.leave() : this.enter();
16629     },
16630     
16631     enter : function () {
16632         
16633         clearTimeout(this.timeout);
16634     
16635         this.hoverState = 'in';
16636     
16637         if (!this.delay || !this.delay.show) {
16638             this.show();
16639             return;
16640         }
16641         var _t = this;
16642         this.timeout = setTimeout(function () {
16643             if (_t.hoverState == 'in') {
16644                 _t.show();
16645             }
16646         }, this.delay.show)
16647     },
16648     
16649     leave : function() {
16650         clearTimeout(this.timeout);
16651     
16652         this.hoverState = 'out';
16653     
16654         if (!this.delay || !this.delay.hide) {
16655             this.hide();
16656             return;
16657         }
16658         var _t = this;
16659         this.timeout = setTimeout(function () {
16660             if (_t.hoverState == 'out') {
16661                 _t.hide();
16662             }
16663         }, this.delay.hide)
16664     },
16665     
16666     show : function (on_el)
16667     {
16668         if (!on_el) {
16669             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16670         }
16671         
16672         // set content.
16673         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16674         if (this.html !== false) {
16675             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16676         }
16677         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16678         if (!this.title.length) {
16679             this.el.select('.popover-title',true).hide();
16680         }
16681         
16682         var placement = typeof this.placement == 'function' ?
16683             this.placement.call(this, this.el, on_el) :
16684             this.placement;
16685             
16686         var autoToken = /\s?auto?\s?/i;
16687         var autoPlace = autoToken.test(placement);
16688         if (autoPlace) {
16689             placement = placement.replace(autoToken, '') || 'top';
16690         }
16691         
16692         //this.el.detach()
16693         //this.el.setXY([0,0]);
16694         this.el.show();
16695         this.el.dom.style.display='block';
16696         this.el.addClass(placement);
16697         
16698         //this.el.appendTo(on_el);
16699         
16700         var p = this.getPosition();
16701         var box = this.el.getBox();
16702         
16703         if (autoPlace) {
16704             // fixme..
16705         }
16706         var align = Roo.bootstrap.Popover.alignment[placement];
16707         this.el.alignTo(on_el, align[0],align[1]);
16708         //var arrow = this.el.select('.arrow',true).first();
16709         //arrow.set(align[2], 
16710         
16711         this.el.addClass('in');
16712         
16713         
16714         if (this.el.hasClass('fade')) {
16715             // fade it?
16716         }
16717         
16718         this.hoverState = 'in';
16719         
16720         this.fireEvent('show', this);
16721         
16722     },
16723     hide : function()
16724     {
16725         this.el.setXY([0,0]);
16726         this.el.removeClass('in');
16727         this.el.hide();
16728         this.hoverState = null;
16729         
16730         this.fireEvent('hide', this);
16731     }
16732     
16733 });
16734
16735 Roo.bootstrap.Popover.alignment = {
16736     'left' : ['r-l', [-10,0], 'right'],
16737     'right' : ['l-r', [10,0], 'left'],
16738     'bottom' : ['t-b', [0,10], 'top'],
16739     'top' : [ 'b-t', [0,-10], 'bottom']
16740 };
16741
16742  /*
16743  * - LGPL
16744  *
16745  * Progress
16746  * 
16747  */
16748
16749 /**
16750  * @class Roo.bootstrap.Progress
16751  * @extends Roo.bootstrap.Component
16752  * Bootstrap Progress class
16753  * @cfg {Boolean} striped striped of the progress bar
16754  * @cfg {Boolean} active animated of the progress bar
16755  * 
16756  * 
16757  * @constructor
16758  * Create a new Progress
16759  * @param {Object} config The config object
16760  */
16761
16762 Roo.bootstrap.Progress = function(config){
16763     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16764 };
16765
16766 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
16767     
16768     striped : false,
16769     active: false,
16770     
16771     getAutoCreate : function(){
16772         var cfg = {
16773             tag: 'div',
16774             cls: 'progress'
16775         };
16776         
16777         
16778         if(this.striped){
16779             cfg.cls += ' progress-striped';
16780         }
16781       
16782         if(this.active){
16783             cfg.cls += ' active';
16784         }
16785         
16786         
16787         return cfg;
16788     }
16789    
16790 });
16791
16792  
16793
16794  /*
16795  * - LGPL
16796  *
16797  * ProgressBar
16798  * 
16799  */
16800
16801 /**
16802  * @class Roo.bootstrap.ProgressBar
16803  * @extends Roo.bootstrap.Component
16804  * Bootstrap ProgressBar class
16805  * @cfg {Number} aria_valuenow aria-value now
16806  * @cfg {Number} aria_valuemin aria-value min
16807  * @cfg {Number} aria_valuemax aria-value max
16808  * @cfg {String} label label for the progress bar
16809  * @cfg {String} panel (success | info | warning | danger )
16810  * @cfg {String} role role of the progress bar
16811  * @cfg {String} sr_only text
16812  * 
16813  * 
16814  * @constructor
16815  * Create a new ProgressBar
16816  * @param {Object} config The config object
16817  */
16818
16819 Roo.bootstrap.ProgressBar = function(config){
16820     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16821 };
16822
16823 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
16824     
16825     aria_valuenow : 0,
16826     aria_valuemin : 0,
16827     aria_valuemax : 100,
16828     label : false,
16829     panel : false,
16830     role : false,
16831     sr_only: false,
16832     
16833     getAutoCreate : function()
16834     {
16835         
16836         var cfg = {
16837             tag: 'div',
16838             cls: 'progress-bar',
16839             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16840         };
16841         
16842         if(this.sr_only){
16843             cfg.cn = {
16844                 tag: 'span',
16845                 cls: 'sr-only',
16846                 html: this.sr_only
16847             }
16848         }
16849         
16850         if(this.role){
16851             cfg.role = this.role;
16852         }
16853         
16854         if(this.aria_valuenow){
16855             cfg['aria-valuenow'] = this.aria_valuenow;
16856         }
16857         
16858         if(this.aria_valuemin){
16859             cfg['aria-valuemin'] = this.aria_valuemin;
16860         }
16861         
16862         if(this.aria_valuemax){
16863             cfg['aria-valuemax'] = this.aria_valuemax;
16864         }
16865         
16866         if(this.label && !this.sr_only){
16867             cfg.html = this.label;
16868         }
16869         
16870         if(this.panel){
16871             cfg.cls += ' progress-bar-' + this.panel;
16872         }
16873         
16874         return cfg;
16875     },
16876     
16877     update : function(aria_valuenow)
16878     {
16879         this.aria_valuenow = aria_valuenow;
16880         
16881         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16882     }
16883    
16884 });
16885
16886  
16887
16888  /*
16889  * - LGPL
16890  *
16891  * column
16892  * 
16893  */
16894
16895 /**
16896  * @class Roo.bootstrap.TabGroup
16897  * @extends Roo.bootstrap.Column
16898  * Bootstrap Column class
16899  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16900  * @cfg {Boolean} carousel true to make the group behave like a carousel
16901  * @cfg {Boolean} bullets show bullets for the panels
16902  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16903  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16904  * @cfg {Boolean} showarrow (true|false) show arrow default true
16905  * 
16906  * @constructor
16907  * Create a new TabGroup
16908  * @param {Object} config The config object
16909  */
16910
16911 Roo.bootstrap.TabGroup = function(config){
16912     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16913     if (!this.navId) {
16914         this.navId = Roo.id();
16915     }
16916     this.tabs = [];
16917     Roo.bootstrap.TabGroup.register(this);
16918     
16919 };
16920
16921 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16922     
16923     carousel : false,
16924     transition : false,
16925     bullets : 0,
16926     timer : 0,
16927     autoslide : false,
16928     slideFn : false,
16929     slideOnTouch : false,
16930     showarrow : true,
16931     
16932     getAutoCreate : function()
16933     {
16934         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16935         
16936         cfg.cls += ' tab-content';
16937         
16938         if (this.carousel) {
16939             cfg.cls += ' carousel slide';
16940             
16941             cfg.cn = [{
16942                cls : 'carousel-inner',
16943                cn : []
16944             }];
16945         
16946             if(this.bullets  && !Roo.isTouch){
16947                 
16948                 var bullets = {
16949                     cls : 'carousel-bullets',
16950                     cn : []
16951                 };
16952                
16953                 if(this.bullets_cls){
16954                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16955                 }
16956                 
16957                 bullets.cn.push({
16958                     cls : 'clear'
16959                 });
16960                 
16961                 cfg.cn[0].cn.push(bullets);
16962             }
16963             
16964             if(this.showarrow){
16965                 cfg.cn[0].cn.push({
16966                     tag : 'div',
16967                     class : 'carousel-arrow',
16968                     cn : [
16969                         {
16970                             tag : 'div',
16971                             class : 'carousel-prev',
16972                             cn : [
16973                                 {
16974                                     tag : 'i',
16975                                     class : 'fa fa-chevron-left'
16976                                 }
16977                             ]
16978                         },
16979                         {
16980                             tag : 'div',
16981                             class : 'carousel-next',
16982                             cn : [
16983                                 {
16984                                     tag : 'i',
16985                                     class : 'fa fa-chevron-right'
16986                                 }
16987                             ]
16988                         }
16989                     ]
16990                 });
16991             }
16992             
16993         }
16994         
16995         return cfg;
16996     },
16997     
16998     initEvents:  function()
16999     {
17000 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17001 //            this.el.on("touchstart", this.onTouchStart, this);
17002 //        }
17003         
17004         if(this.autoslide){
17005             var _this = this;
17006             
17007             this.slideFn = window.setInterval(function() {
17008                 _this.showPanelNext();
17009             }, this.timer);
17010         }
17011         
17012         if(this.showarrow){
17013             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17014             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17015         }
17016         
17017         
17018     },
17019     
17020 //    onTouchStart : function(e, el, o)
17021 //    {
17022 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17023 //            return;
17024 //        }
17025 //        
17026 //        this.showPanelNext();
17027 //    },
17028     
17029     
17030     getChildContainer : function()
17031     {
17032         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17033     },
17034     
17035     /**
17036     * register a Navigation item
17037     * @param {Roo.bootstrap.NavItem} the navitem to add
17038     */
17039     register : function(item)
17040     {
17041         this.tabs.push( item);
17042         item.navId = this.navId; // not really needed..
17043         this.addBullet();
17044     
17045     },
17046     
17047     getActivePanel : function()
17048     {
17049         var r = false;
17050         Roo.each(this.tabs, function(t) {
17051             if (t.active) {
17052                 r = t;
17053                 return false;
17054             }
17055             return null;
17056         });
17057         return r;
17058         
17059     },
17060     getPanelByName : function(n)
17061     {
17062         var r = false;
17063         Roo.each(this.tabs, function(t) {
17064             if (t.tabId == n) {
17065                 r = t;
17066                 return false;
17067             }
17068             return null;
17069         });
17070         return r;
17071     },
17072     indexOfPanel : function(p)
17073     {
17074         var r = false;
17075         Roo.each(this.tabs, function(t,i) {
17076             if (t.tabId == p.tabId) {
17077                 r = i;
17078                 return false;
17079             }
17080             return null;
17081         });
17082         return r;
17083     },
17084     /**
17085      * show a specific panel
17086      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17087      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17088      */
17089     showPanel : function (pan)
17090     {
17091         if(this.transition || typeof(pan) == 'undefined'){
17092             Roo.log("waiting for the transitionend");
17093             return;
17094         }
17095         
17096         if (typeof(pan) == 'number') {
17097             pan = this.tabs[pan];
17098         }
17099         
17100         if (typeof(pan) == 'string') {
17101             pan = this.getPanelByName(pan);
17102         }
17103         
17104         var cur = this.getActivePanel();
17105         
17106         if(!pan || !cur){
17107             Roo.log('pan or acitve pan is undefined');
17108             return false;
17109         }
17110         
17111         if (pan.tabId == this.getActivePanel().tabId) {
17112             return true;
17113         }
17114         
17115         if (false === cur.fireEvent('beforedeactivate')) {
17116             return false;
17117         }
17118         
17119         if(this.bullets > 0 && !Roo.isTouch){
17120             this.setActiveBullet(this.indexOfPanel(pan));
17121         }
17122         
17123         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17124             
17125             this.transition = true;
17126             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17127             var lr = dir == 'next' ? 'left' : 'right';
17128             pan.el.addClass(dir); // or prev
17129             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17130             cur.el.addClass(lr); // or right
17131             pan.el.addClass(lr);
17132             
17133             var _this = this;
17134             cur.el.on('transitionend', function() {
17135                 Roo.log("trans end?");
17136                 
17137                 pan.el.removeClass([lr,dir]);
17138                 pan.setActive(true);
17139                 
17140                 cur.el.removeClass([lr]);
17141                 cur.setActive(false);
17142                 
17143                 _this.transition = false;
17144                 
17145             }, this, { single:  true } );
17146             
17147             return true;
17148         }
17149         
17150         cur.setActive(false);
17151         pan.setActive(true);
17152         
17153         return true;
17154         
17155     },
17156     showPanelNext : function()
17157     {
17158         var i = this.indexOfPanel(this.getActivePanel());
17159         
17160         if (i >= this.tabs.length - 1 && !this.autoslide) {
17161             return;
17162         }
17163         
17164         if (i >= this.tabs.length - 1 && this.autoslide) {
17165             i = -1;
17166         }
17167         
17168         this.showPanel(this.tabs[i+1]);
17169     },
17170     
17171     showPanelPrev : function()
17172     {
17173         var i = this.indexOfPanel(this.getActivePanel());
17174         
17175         if (i  < 1 && !this.autoslide) {
17176             return;
17177         }
17178         
17179         if (i < 1 && this.autoslide) {
17180             i = this.tabs.length;
17181         }
17182         
17183         this.showPanel(this.tabs[i-1]);
17184     },
17185     
17186     
17187     addBullet: function()
17188     {
17189         if(!this.bullets || Roo.isTouch){
17190             return;
17191         }
17192         var ctr = this.el.select('.carousel-bullets',true).first();
17193         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17194         var bullet = ctr.createChild({
17195             cls : 'bullet bullet-' + i
17196         },ctr.dom.lastChild);
17197         
17198         
17199         var _this = this;
17200         
17201         bullet.on('click', (function(e, el, o, ii, t){
17202
17203             e.preventDefault();
17204
17205             this.showPanel(ii);
17206
17207             if(this.autoslide && this.slideFn){
17208                 clearInterval(this.slideFn);
17209                 this.slideFn = window.setInterval(function() {
17210                     _this.showPanelNext();
17211                 }, this.timer);
17212             }
17213
17214         }).createDelegate(this, [i, bullet], true));
17215                 
17216         
17217     },
17218      
17219     setActiveBullet : function(i)
17220     {
17221         if(Roo.isTouch){
17222             return;
17223         }
17224         
17225         Roo.each(this.el.select('.bullet', true).elements, function(el){
17226             el.removeClass('selected');
17227         });
17228
17229         var bullet = this.el.select('.bullet-' + i, true).first();
17230         
17231         if(!bullet){
17232             return;
17233         }
17234         
17235         bullet.addClass('selected');
17236     }
17237     
17238     
17239   
17240 });
17241
17242  
17243
17244  
17245  
17246 Roo.apply(Roo.bootstrap.TabGroup, {
17247     
17248     groups: {},
17249      /**
17250     * register a Navigation Group
17251     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17252     */
17253     register : function(navgrp)
17254     {
17255         this.groups[navgrp.navId] = navgrp;
17256         
17257     },
17258     /**
17259     * fetch a Navigation Group based on the navigation ID
17260     * if one does not exist , it will get created.
17261     * @param {string} the navgroup to add
17262     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17263     */
17264     get: function(navId) {
17265         if (typeof(this.groups[navId]) == 'undefined') {
17266             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17267         }
17268         return this.groups[navId] ;
17269     }
17270     
17271     
17272     
17273 });
17274
17275  /*
17276  * - LGPL
17277  *
17278  * TabPanel
17279  * 
17280  */
17281
17282 /**
17283  * @class Roo.bootstrap.TabPanel
17284  * @extends Roo.bootstrap.Component
17285  * Bootstrap TabPanel class
17286  * @cfg {Boolean} active panel active
17287  * @cfg {String} html panel content
17288  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17289  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17290  * @cfg {String} href click to link..
17291  * 
17292  * 
17293  * @constructor
17294  * Create a new TabPanel
17295  * @param {Object} config The config object
17296  */
17297
17298 Roo.bootstrap.TabPanel = function(config){
17299     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17300     this.addEvents({
17301         /**
17302              * @event changed
17303              * Fires when the active status changes
17304              * @param {Roo.bootstrap.TabPanel} this
17305              * @param {Boolean} state the new state
17306             
17307          */
17308         'changed': true,
17309         /**
17310              * @event beforedeactivate
17311              * Fires before a tab is de-activated - can be used to do validation on a form.
17312              * @param {Roo.bootstrap.TabPanel} this
17313              * @return {Boolean} false if there is an error
17314             
17315          */
17316         'beforedeactivate': true
17317      });
17318     
17319     this.tabId = this.tabId || Roo.id();
17320   
17321 };
17322
17323 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17324     
17325     active: false,
17326     html: false,
17327     tabId: false,
17328     navId : false,
17329     href : '',
17330     
17331     getAutoCreate : function(){
17332         var cfg = {
17333             tag: 'div',
17334             // item is needed for carousel - not sure if it has any effect otherwise
17335             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17336             html: this.html || ''
17337         };
17338         
17339         if(this.active){
17340             cfg.cls += ' active';
17341         }
17342         
17343         if(this.tabId){
17344             cfg.tabId = this.tabId;
17345         }
17346         
17347         
17348         return cfg;
17349     },
17350     
17351     initEvents:  function()
17352     {
17353         var p = this.parent();
17354         
17355         this.navId = this.navId || p.navId;
17356         
17357         if (typeof(this.navId) != 'undefined') {
17358             // not really needed.. but just in case.. parent should be a NavGroup.
17359             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17360             
17361             tg.register(this);
17362             
17363             var i = tg.tabs.length - 1;
17364             
17365             if(this.active && tg.bullets > 0 && i < tg.bullets){
17366                 tg.setActiveBullet(i);
17367             }
17368         }
17369         
17370         this.el.on('click', this.onClick, this);
17371         
17372         if(Roo.isTouch){
17373             this.el.on("touchstart", this.onTouchStart, this);
17374             this.el.on("touchmove", this.onTouchMove, this);
17375             this.el.on("touchend", this.onTouchEnd, this);
17376         }
17377         
17378     },
17379     
17380     onRender : function(ct, position)
17381     {
17382         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17383     },
17384     
17385     setActive : function(state)
17386     {
17387         Roo.log("panel - set active " + this.tabId + "=" + state);
17388         
17389         this.active = state;
17390         if (!state) {
17391             this.el.removeClass('active');
17392             
17393         } else  if (!this.el.hasClass('active')) {
17394             this.el.addClass('active');
17395         }
17396         
17397         this.fireEvent('changed', this, state);
17398     },
17399     
17400     onClick : function(e)
17401     {
17402         e.preventDefault();
17403         
17404         if(!this.href.length){
17405             return;
17406         }
17407         
17408         window.location.href = this.href;
17409     },
17410     
17411     startX : 0,
17412     startY : 0,
17413     endX : 0,
17414     endY : 0,
17415     swiping : false,
17416     
17417     onTouchStart : function(e)
17418     {
17419         this.swiping = false;
17420         
17421         this.startX = e.browserEvent.touches[0].clientX;
17422         this.startY = e.browserEvent.touches[0].clientY;
17423     },
17424     
17425     onTouchMove : function(e)
17426     {
17427         this.swiping = true;
17428         
17429         this.endX = e.browserEvent.touches[0].clientX;
17430         this.endY = e.browserEvent.touches[0].clientY;
17431     },
17432     
17433     onTouchEnd : function(e)
17434     {
17435         if(!this.swiping){
17436             this.onClick(e);
17437             return;
17438         }
17439         
17440         var tabGroup = this.parent();
17441         
17442         if(this.endX > this.startX){ // swiping right
17443             tabGroup.showPanelPrev();
17444             return;
17445         }
17446         
17447         if(this.startX > this.endX){ // swiping left
17448             tabGroup.showPanelNext();
17449             return;
17450         }
17451     }
17452     
17453     
17454 });
17455  
17456
17457  
17458
17459  /*
17460  * - LGPL
17461  *
17462  * DateField
17463  * 
17464  */
17465
17466 /**
17467  * @class Roo.bootstrap.DateField
17468  * @extends Roo.bootstrap.Input
17469  * Bootstrap DateField class
17470  * @cfg {Number} weekStart default 0
17471  * @cfg {String} viewMode default empty, (months|years)
17472  * @cfg {String} minViewMode default empty, (months|years)
17473  * @cfg {Number} startDate default -Infinity
17474  * @cfg {Number} endDate default Infinity
17475  * @cfg {Boolean} todayHighlight default false
17476  * @cfg {Boolean} todayBtn default false
17477  * @cfg {Boolean} calendarWeeks default false
17478  * @cfg {Object} daysOfWeekDisabled default empty
17479  * @cfg {Boolean} singleMode default false (true | false)
17480  * 
17481  * @cfg {Boolean} keyboardNavigation default true
17482  * @cfg {String} language default en
17483  * 
17484  * @constructor
17485  * Create a new DateField
17486  * @param {Object} config The config object
17487  */
17488
17489 Roo.bootstrap.DateField = function(config){
17490     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17491      this.addEvents({
17492             /**
17493              * @event show
17494              * Fires when this field show.
17495              * @param {Roo.bootstrap.DateField} this
17496              * @param {Mixed} date The date value
17497              */
17498             show : true,
17499             /**
17500              * @event show
17501              * Fires when this field hide.
17502              * @param {Roo.bootstrap.DateField} this
17503              * @param {Mixed} date The date value
17504              */
17505             hide : true,
17506             /**
17507              * @event select
17508              * Fires when select a date.
17509              * @param {Roo.bootstrap.DateField} this
17510              * @param {Mixed} date The date value
17511              */
17512             select : true,
17513             /**
17514              * @event beforeselect
17515              * Fires when before select a date.
17516              * @param {Roo.bootstrap.DateField} this
17517              * @param {Mixed} date The date value
17518              */
17519             beforeselect : true
17520         });
17521 };
17522
17523 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
17524     
17525     /**
17526      * @cfg {String} format
17527      * The default date format string which can be overriden for localization support.  The format must be
17528      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17529      */
17530     format : "m/d/y",
17531     /**
17532      * @cfg {String} altFormats
17533      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17534      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17535      */
17536     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17537     
17538     weekStart : 0,
17539     
17540     viewMode : '',
17541     
17542     minViewMode : '',
17543     
17544     todayHighlight : false,
17545     
17546     todayBtn: false,
17547     
17548     language: 'en',
17549     
17550     keyboardNavigation: true,
17551     
17552     calendarWeeks: false,
17553     
17554     startDate: -Infinity,
17555     
17556     endDate: Infinity,
17557     
17558     daysOfWeekDisabled: [],
17559     
17560     _events: [],
17561     
17562     singleMode : false,
17563     
17564     UTCDate: function()
17565     {
17566         return new Date(Date.UTC.apply(Date, arguments));
17567     },
17568     
17569     UTCToday: function()
17570     {
17571         var today = new Date();
17572         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17573     },
17574     
17575     getDate: function() {
17576             var d = this.getUTCDate();
17577             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17578     },
17579     
17580     getUTCDate: function() {
17581             return this.date;
17582     },
17583     
17584     setDate: function(d) {
17585             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17586     },
17587     
17588     setUTCDate: function(d) {
17589             this.date = d;
17590             this.setValue(this.formatDate(this.date));
17591     },
17592         
17593     onRender: function(ct, position)
17594     {
17595         
17596         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17597         
17598         this.language = this.language || 'en';
17599         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17600         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17601         
17602         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17603         this.format = this.format || 'm/d/y';
17604         this.isInline = false;
17605         this.isInput = true;
17606         this.component = this.el.select('.add-on', true).first() || false;
17607         this.component = (this.component && this.component.length === 0) ? false : this.component;
17608         this.hasInput = this.component && this.inputEl().length;
17609         
17610         if (typeof(this.minViewMode === 'string')) {
17611             switch (this.minViewMode) {
17612                 case 'months':
17613                     this.minViewMode = 1;
17614                     break;
17615                 case 'years':
17616                     this.minViewMode = 2;
17617                     break;
17618                 default:
17619                     this.minViewMode = 0;
17620                     break;
17621             }
17622         }
17623         
17624         if (typeof(this.viewMode === 'string')) {
17625             switch (this.viewMode) {
17626                 case 'months':
17627                     this.viewMode = 1;
17628                     break;
17629                 case 'years':
17630                     this.viewMode = 2;
17631                     break;
17632                 default:
17633                     this.viewMode = 0;
17634                     break;
17635             }
17636         }
17637                 
17638         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17639         
17640 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17641         
17642         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17643         
17644         this.picker().on('mousedown', this.onMousedown, this);
17645         this.picker().on('click', this.onClick, this);
17646         
17647         this.picker().addClass('datepicker-dropdown');
17648         
17649         this.startViewMode = this.viewMode;
17650         
17651         if(this.singleMode){
17652             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17653                 v.setVisibilityMode(Roo.Element.DISPLAY);
17654                 v.hide();
17655             });
17656             
17657             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17658                 v.setStyle('width', '189px');
17659             });
17660         }
17661         
17662         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17663             if(!this.calendarWeeks){
17664                 v.remove();
17665                 return;
17666             }
17667             
17668             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17669             v.attr('colspan', function(i, val){
17670                 return parseInt(val) + 1;
17671             });
17672         });
17673                         
17674         
17675         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17676         
17677         this.setStartDate(this.startDate);
17678         this.setEndDate(this.endDate);
17679         
17680         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17681         
17682         this.fillDow();
17683         this.fillMonths();
17684         this.update();
17685         this.showMode();
17686         
17687         if(this.isInline) {
17688             this.show();
17689         }
17690     },
17691     
17692     picker : function()
17693     {
17694         return this.pickerEl;
17695 //        return this.el.select('.datepicker', true).first();
17696     },
17697     
17698     fillDow: function()
17699     {
17700         var dowCnt = this.weekStart;
17701         
17702         var dow = {
17703             tag: 'tr',
17704             cn: [
17705                 
17706             ]
17707         };
17708         
17709         if(this.calendarWeeks){
17710             dow.cn.push({
17711                 tag: 'th',
17712                 cls: 'cw',
17713                 html: '&nbsp;'
17714             })
17715         }
17716         
17717         while (dowCnt < this.weekStart + 7) {
17718             dow.cn.push({
17719                 tag: 'th',
17720                 cls: 'dow',
17721                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17722             });
17723         }
17724         
17725         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17726     },
17727     
17728     fillMonths: function()
17729     {    
17730         var i = 0;
17731         var months = this.picker().select('>.datepicker-months td', true).first();
17732         
17733         months.dom.innerHTML = '';
17734         
17735         while (i < 12) {
17736             var month = {
17737                 tag: 'span',
17738                 cls: 'month',
17739                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17740             };
17741             
17742             months.createChild(month);
17743         }
17744         
17745     },
17746     
17747     update: function()
17748     {
17749         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;
17750         
17751         if (this.date < this.startDate) {
17752             this.viewDate = new Date(this.startDate);
17753         } else if (this.date > this.endDate) {
17754             this.viewDate = new Date(this.endDate);
17755         } else {
17756             this.viewDate = new Date(this.date);
17757         }
17758         
17759         this.fill();
17760     },
17761     
17762     fill: function() 
17763     {
17764         var d = new Date(this.viewDate),
17765                 year = d.getUTCFullYear(),
17766                 month = d.getUTCMonth(),
17767                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17768                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17769                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17770                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17771                 currentDate = this.date && this.date.valueOf(),
17772                 today = this.UTCToday();
17773         
17774         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17775         
17776 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17777         
17778 //        this.picker.select('>tfoot th.today').
17779 //                                              .text(dates[this.language].today)
17780 //                                              .toggle(this.todayBtn !== false);
17781     
17782         this.updateNavArrows();
17783         this.fillMonths();
17784                                                 
17785         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17786         
17787         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17788          
17789         prevMonth.setUTCDate(day);
17790         
17791         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17792         
17793         var nextMonth = new Date(prevMonth);
17794         
17795         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17796         
17797         nextMonth = nextMonth.valueOf();
17798         
17799         var fillMonths = false;
17800         
17801         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17802         
17803         while(prevMonth.valueOf() < nextMonth) {
17804             var clsName = '';
17805             
17806             if (prevMonth.getUTCDay() === this.weekStart) {
17807                 if(fillMonths){
17808                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17809                 }
17810                     
17811                 fillMonths = {
17812                     tag: 'tr',
17813                     cn: []
17814                 };
17815                 
17816                 if(this.calendarWeeks){
17817                     // ISO 8601: First week contains first thursday.
17818                     // ISO also states week starts on Monday, but we can be more abstract here.
17819                     var
17820                     // Start of current week: based on weekstart/current date
17821                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17822                     // Thursday of this week
17823                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17824                     // First Thursday of year, year from thursday
17825                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17826                     // Calendar week: ms between thursdays, div ms per day, div 7 days
17827                     calWeek =  (th - yth) / 864e5 / 7 + 1;
17828                     
17829                     fillMonths.cn.push({
17830                         tag: 'td',
17831                         cls: 'cw',
17832                         html: calWeek
17833                     });
17834                 }
17835             }
17836             
17837             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17838                 clsName += ' old';
17839             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17840                 clsName += ' new';
17841             }
17842             if (this.todayHighlight &&
17843                 prevMonth.getUTCFullYear() == today.getFullYear() &&
17844                 prevMonth.getUTCMonth() == today.getMonth() &&
17845                 prevMonth.getUTCDate() == today.getDate()) {
17846                 clsName += ' today';
17847             }
17848             
17849             if (currentDate && prevMonth.valueOf() === currentDate) {
17850                 clsName += ' active';
17851             }
17852             
17853             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17854                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17855                     clsName += ' disabled';
17856             }
17857             
17858             fillMonths.cn.push({
17859                 tag: 'td',
17860                 cls: 'day ' + clsName,
17861                 html: prevMonth.getDate()
17862             });
17863             
17864             prevMonth.setDate(prevMonth.getDate()+1);
17865         }
17866           
17867         var currentYear = this.date && this.date.getUTCFullYear();
17868         var currentMonth = this.date && this.date.getUTCMonth();
17869         
17870         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17871         
17872         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17873             v.removeClass('active');
17874             
17875             if(currentYear === year && k === currentMonth){
17876                 v.addClass('active');
17877             }
17878             
17879             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17880                 v.addClass('disabled');
17881             }
17882             
17883         });
17884         
17885         
17886         year = parseInt(year/10, 10) * 10;
17887         
17888         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17889         
17890         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17891         
17892         year -= 1;
17893         for (var i = -1; i < 11; i++) {
17894             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17895                 tag: 'span',
17896                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17897                 html: year
17898             });
17899             
17900             year += 1;
17901         }
17902     },
17903     
17904     showMode: function(dir) 
17905     {
17906         if (dir) {
17907             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17908         }
17909         
17910         Roo.each(this.picker().select('>div',true).elements, function(v){
17911             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17912             v.hide();
17913         });
17914         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17915     },
17916     
17917     place: function()
17918     {
17919         if(this.isInline) {
17920             return;
17921         }
17922         
17923         this.picker().removeClass(['bottom', 'top']);
17924         
17925         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17926             /*
17927              * place to the top of element!
17928              *
17929              */
17930             
17931             this.picker().addClass('top');
17932             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17933             
17934             return;
17935         }
17936         
17937         this.picker().addClass('bottom');
17938         
17939         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17940     },
17941     
17942     parseDate : function(value)
17943     {
17944         if(!value || value instanceof Date){
17945             return value;
17946         }
17947         var v = Date.parseDate(value, this.format);
17948         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17949             v = Date.parseDate(value, 'Y-m-d');
17950         }
17951         if(!v && this.altFormats){
17952             if(!this.altFormatsArray){
17953                 this.altFormatsArray = this.altFormats.split("|");
17954             }
17955             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17956                 v = Date.parseDate(value, this.altFormatsArray[i]);
17957             }
17958         }
17959         return v;
17960     },
17961     
17962     formatDate : function(date, fmt)
17963     {   
17964         return (!date || !(date instanceof Date)) ?
17965         date : date.dateFormat(fmt || this.format);
17966     },
17967     
17968     onFocus : function()
17969     {
17970         Roo.bootstrap.DateField.superclass.onFocus.call(this);
17971         this.show();
17972     },
17973     
17974     onBlur : function()
17975     {
17976         Roo.bootstrap.DateField.superclass.onBlur.call(this);
17977         
17978         var d = this.inputEl().getValue();
17979         
17980         this.setValue(d);
17981                 
17982         this.hide();
17983     },
17984     
17985     show : function()
17986     {
17987         this.picker().show();
17988         this.update();
17989         this.place();
17990         
17991         this.fireEvent('show', this, this.date);
17992     },
17993     
17994     hide : function()
17995     {
17996         if(this.isInline) {
17997             return;
17998         }
17999         this.picker().hide();
18000         this.viewMode = this.startViewMode;
18001         this.showMode();
18002         
18003         this.fireEvent('hide', this, this.date);
18004         
18005     },
18006     
18007     onMousedown: function(e)
18008     {
18009         e.stopPropagation();
18010         e.preventDefault();
18011     },
18012     
18013     keyup: function(e)
18014     {
18015         Roo.bootstrap.DateField.superclass.keyup.call(this);
18016         this.update();
18017     },
18018
18019     setValue: function(v)
18020     {
18021         if(this.fireEvent('beforeselect', this, v) !== false){
18022             var d = new Date(this.parseDate(v) ).clearTime();
18023         
18024             if(isNaN(d.getTime())){
18025                 this.date = this.viewDate = '';
18026                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18027                 return;
18028             }
18029
18030             v = this.formatDate(d);
18031
18032             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18033
18034             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18035
18036             this.update();
18037
18038             this.fireEvent('select', this, this.date);
18039         }
18040     },
18041     
18042     getValue: function()
18043     {
18044         return this.formatDate(this.date);
18045     },
18046     
18047     fireKey: function(e)
18048     {
18049         if (!this.picker().isVisible()){
18050             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18051                 this.show();
18052             }
18053             return;
18054         }
18055         
18056         var dateChanged = false,
18057         dir, day, month,
18058         newDate, newViewDate;
18059         
18060         switch(e.keyCode){
18061             case 27: // escape
18062                 this.hide();
18063                 e.preventDefault();
18064                 break;
18065             case 37: // left
18066             case 39: // right
18067                 if (!this.keyboardNavigation) {
18068                     break;
18069                 }
18070                 dir = e.keyCode == 37 ? -1 : 1;
18071                 
18072                 if (e.ctrlKey){
18073                     newDate = this.moveYear(this.date, dir);
18074                     newViewDate = this.moveYear(this.viewDate, dir);
18075                 } else if (e.shiftKey){
18076                     newDate = this.moveMonth(this.date, dir);
18077                     newViewDate = this.moveMonth(this.viewDate, dir);
18078                 } else {
18079                     newDate = new Date(this.date);
18080                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18081                     newViewDate = new Date(this.viewDate);
18082                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18083                 }
18084                 if (this.dateWithinRange(newDate)){
18085                     this.date = newDate;
18086                     this.viewDate = newViewDate;
18087                     this.setValue(this.formatDate(this.date));
18088 //                    this.update();
18089                     e.preventDefault();
18090                     dateChanged = true;
18091                 }
18092                 break;
18093             case 38: // up
18094             case 40: // down
18095                 if (!this.keyboardNavigation) {
18096                     break;
18097                 }
18098                 dir = e.keyCode == 38 ? -1 : 1;
18099                 if (e.ctrlKey){
18100                     newDate = this.moveYear(this.date, dir);
18101                     newViewDate = this.moveYear(this.viewDate, dir);
18102                 } else if (e.shiftKey){
18103                     newDate = this.moveMonth(this.date, dir);
18104                     newViewDate = this.moveMonth(this.viewDate, dir);
18105                 } else {
18106                     newDate = new Date(this.date);
18107                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18108                     newViewDate = new Date(this.viewDate);
18109                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18110                 }
18111                 if (this.dateWithinRange(newDate)){
18112                     this.date = newDate;
18113                     this.viewDate = newViewDate;
18114                     this.setValue(this.formatDate(this.date));
18115 //                    this.update();
18116                     e.preventDefault();
18117                     dateChanged = true;
18118                 }
18119                 break;
18120             case 13: // enter
18121                 this.setValue(this.formatDate(this.date));
18122                 this.hide();
18123                 e.preventDefault();
18124                 break;
18125             case 9: // tab
18126                 this.setValue(this.formatDate(this.date));
18127                 this.hide();
18128                 break;
18129             case 16: // shift
18130             case 17: // ctrl
18131             case 18: // alt
18132                 break;
18133             default :
18134                 this.hide();
18135                 
18136         }
18137     },
18138     
18139     
18140     onClick: function(e) 
18141     {
18142         e.stopPropagation();
18143         e.preventDefault();
18144         
18145         var target = e.getTarget();
18146         
18147         if(target.nodeName.toLowerCase() === 'i'){
18148             target = Roo.get(target).dom.parentNode;
18149         }
18150         
18151         var nodeName = target.nodeName;
18152         var className = target.className;
18153         var html = target.innerHTML;
18154         //Roo.log(nodeName);
18155         
18156         switch(nodeName.toLowerCase()) {
18157             case 'th':
18158                 switch(className) {
18159                     case 'switch':
18160                         this.showMode(1);
18161                         break;
18162                     case 'prev':
18163                     case 'next':
18164                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18165                         switch(this.viewMode){
18166                                 case 0:
18167                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18168                                         break;
18169                                 case 1:
18170                                 case 2:
18171                                         this.viewDate = this.moveYear(this.viewDate, dir);
18172                                         break;
18173                         }
18174                         this.fill();
18175                         break;
18176                     case 'today':
18177                         var date = new Date();
18178                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18179 //                        this.fill()
18180                         this.setValue(this.formatDate(this.date));
18181                         
18182                         this.hide();
18183                         break;
18184                 }
18185                 break;
18186             case 'span':
18187                 if (className.indexOf('disabled') < 0) {
18188                     this.viewDate.setUTCDate(1);
18189                     if (className.indexOf('month') > -1) {
18190                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18191                     } else {
18192                         var year = parseInt(html, 10) || 0;
18193                         this.viewDate.setUTCFullYear(year);
18194                         
18195                     }
18196                     
18197                     if(this.singleMode){
18198                         this.setValue(this.formatDate(this.viewDate));
18199                         this.hide();
18200                         return;
18201                     }
18202                     
18203                     this.showMode(-1);
18204                     this.fill();
18205                 }
18206                 break;
18207                 
18208             case 'td':
18209                 //Roo.log(className);
18210                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18211                     var day = parseInt(html, 10) || 1;
18212                     var year = this.viewDate.getUTCFullYear(),
18213                         month = this.viewDate.getUTCMonth();
18214
18215                     if (className.indexOf('old') > -1) {
18216                         if(month === 0 ){
18217                             month = 11;
18218                             year -= 1;
18219                         }else{
18220                             month -= 1;
18221                         }
18222                     } else if (className.indexOf('new') > -1) {
18223                         if (month == 11) {
18224                             month = 0;
18225                             year += 1;
18226                         } else {
18227                             month += 1;
18228                         }
18229                     }
18230                     //Roo.log([year,month,day]);
18231                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18232                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18233 //                    this.fill();
18234                     //Roo.log(this.formatDate(this.date));
18235                     this.setValue(this.formatDate(this.date));
18236                     this.hide();
18237                 }
18238                 break;
18239         }
18240     },
18241     
18242     setStartDate: function(startDate)
18243     {
18244         this.startDate = startDate || -Infinity;
18245         if (this.startDate !== -Infinity) {
18246             this.startDate = this.parseDate(this.startDate);
18247         }
18248         this.update();
18249         this.updateNavArrows();
18250     },
18251
18252     setEndDate: function(endDate)
18253     {
18254         this.endDate = endDate || Infinity;
18255         if (this.endDate !== Infinity) {
18256             this.endDate = this.parseDate(this.endDate);
18257         }
18258         this.update();
18259         this.updateNavArrows();
18260     },
18261     
18262     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18263     {
18264         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18265         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18266             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18267         }
18268         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18269             return parseInt(d, 10);
18270         });
18271         this.update();
18272         this.updateNavArrows();
18273     },
18274     
18275     updateNavArrows: function() 
18276     {
18277         if(this.singleMode){
18278             return;
18279         }
18280         
18281         var d = new Date(this.viewDate),
18282         year = d.getUTCFullYear(),
18283         month = d.getUTCMonth();
18284         
18285         Roo.each(this.picker().select('.prev', true).elements, function(v){
18286             v.show();
18287             switch (this.viewMode) {
18288                 case 0:
18289
18290                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18291                         v.hide();
18292                     }
18293                     break;
18294                 case 1:
18295                 case 2:
18296                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18297                         v.hide();
18298                     }
18299                     break;
18300             }
18301         });
18302         
18303         Roo.each(this.picker().select('.next', true).elements, function(v){
18304             v.show();
18305             switch (this.viewMode) {
18306                 case 0:
18307
18308                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18309                         v.hide();
18310                     }
18311                     break;
18312                 case 1:
18313                 case 2:
18314                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18315                         v.hide();
18316                     }
18317                     break;
18318             }
18319         })
18320     },
18321     
18322     moveMonth: function(date, dir)
18323     {
18324         if (!dir) {
18325             return date;
18326         }
18327         var new_date = new Date(date.valueOf()),
18328         day = new_date.getUTCDate(),
18329         month = new_date.getUTCMonth(),
18330         mag = Math.abs(dir),
18331         new_month, test;
18332         dir = dir > 0 ? 1 : -1;
18333         if (mag == 1){
18334             test = dir == -1
18335             // If going back one month, make sure month is not current month
18336             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18337             ? function(){
18338                 return new_date.getUTCMonth() == month;
18339             }
18340             // If going forward one month, make sure month is as expected
18341             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18342             : function(){
18343                 return new_date.getUTCMonth() != new_month;
18344             };
18345             new_month = month + dir;
18346             new_date.setUTCMonth(new_month);
18347             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18348             if (new_month < 0 || new_month > 11) {
18349                 new_month = (new_month + 12) % 12;
18350             }
18351         } else {
18352             // For magnitudes >1, move one month at a time...
18353             for (var i=0; i<mag; i++) {
18354                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18355                 new_date = this.moveMonth(new_date, dir);
18356             }
18357             // ...then reset the day, keeping it in the new month
18358             new_month = new_date.getUTCMonth();
18359             new_date.setUTCDate(day);
18360             test = function(){
18361                 return new_month != new_date.getUTCMonth();
18362             };
18363         }
18364         // Common date-resetting loop -- if date is beyond end of month, make it
18365         // end of month
18366         while (test()){
18367             new_date.setUTCDate(--day);
18368             new_date.setUTCMonth(new_month);
18369         }
18370         return new_date;
18371     },
18372
18373     moveYear: function(date, dir)
18374     {
18375         return this.moveMonth(date, dir*12);
18376     },
18377
18378     dateWithinRange: function(date)
18379     {
18380         return date >= this.startDate && date <= this.endDate;
18381     },
18382
18383     
18384     remove: function() 
18385     {
18386         this.picker().remove();
18387     },
18388     
18389     validateValue : function(value)
18390     {
18391         if(value.length < 1)  {
18392             if(this.allowBlank){
18393                 return true;
18394             }
18395             return false;
18396         }
18397         
18398         if(value.length < this.minLength){
18399             return false;
18400         }
18401         if(value.length > this.maxLength){
18402             return false;
18403         }
18404         if(this.vtype){
18405             var vt = Roo.form.VTypes;
18406             if(!vt[this.vtype](value, this)){
18407                 return false;
18408             }
18409         }
18410         if(typeof this.validator == "function"){
18411             var msg = this.validator(value);
18412             if(msg !== true){
18413                 return false;
18414             }
18415         }
18416         
18417         if(this.regex && !this.regex.test(value)){
18418             return false;
18419         }
18420         
18421         if(typeof(this.parseDate(value)) == 'undefined'){
18422             return false;
18423         }
18424         
18425         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18426             return false;
18427         }      
18428         
18429         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18430             return false;
18431         } 
18432         
18433         
18434         return true;
18435     }
18436    
18437 });
18438
18439 Roo.apply(Roo.bootstrap.DateField,  {
18440     
18441     head : {
18442         tag: 'thead',
18443         cn: [
18444         {
18445             tag: 'tr',
18446             cn: [
18447             {
18448                 tag: 'th',
18449                 cls: 'prev',
18450                 html: '<i class="fa fa-arrow-left"/>'
18451             },
18452             {
18453                 tag: 'th',
18454                 cls: 'switch',
18455                 colspan: '5'
18456             },
18457             {
18458                 tag: 'th',
18459                 cls: 'next',
18460                 html: '<i class="fa fa-arrow-right"/>'
18461             }
18462
18463             ]
18464         }
18465         ]
18466     },
18467     
18468     content : {
18469         tag: 'tbody',
18470         cn: [
18471         {
18472             tag: 'tr',
18473             cn: [
18474             {
18475                 tag: 'td',
18476                 colspan: '7'
18477             }
18478             ]
18479         }
18480         ]
18481     },
18482     
18483     footer : {
18484         tag: 'tfoot',
18485         cn: [
18486         {
18487             tag: 'tr',
18488             cn: [
18489             {
18490                 tag: 'th',
18491                 colspan: '7',
18492                 cls: 'today'
18493             }
18494                     
18495             ]
18496         }
18497         ]
18498     },
18499     
18500     dates:{
18501         en: {
18502             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18503             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18504             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18505             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18506             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18507             today: "Today"
18508         }
18509     },
18510     
18511     modes: [
18512     {
18513         clsName: 'days',
18514         navFnc: 'Month',
18515         navStep: 1
18516     },
18517     {
18518         clsName: 'months',
18519         navFnc: 'FullYear',
18520         navStep: 1
18521     },
18522     {
18523         clsName: 'years',
18524         navFnc: 'FullYear',
18525         navStep: 10
18526     }]
18527 });
18528
18529 Roo.apply(Roo.bootstrap.DateField,  {
18530   
18531     template : {
18532         tag: 'div',
18533         cls: 'datepicker dropdown-menu roo-dynamic',
18534         cn: [
18535         {
18536             tag: 'div',
18537             cls: 'datepicker-days',
18538             cn: [
18539             {
18540                 tag: 'table',
18541                 cls: 'table-condensed',
18542                 cn:[
18543                 Roo.bootstrap.DateField.head,
18544                 {
18545                     tag: 'tbody'
18546                 },
18547                 Roo.bootstrap.DateField.footer
18548                 ]
18549             }
18550             ]
18551         },
18552         {
18553             tag: 'div',
18554             cls: 'datepicker-months',
18555             cn: [
18556             {
18557                 tag: 'table',
18558                 cls: 'table-condensed',
18559                 cn:[
18560                 Roo.bootstrap.DateField.head,
18561                 Roo.bootstrap.DateField.content,
18562                 Roo.bootstrap.DateField.footer
18563                 ]
18564             }
18565             ]
18566         },
18567         {
18568             tag: 'div',
18569             cls: 'datepicker-years',
18570             cn: [
18571             {
18572                 tag: 'table',
18573                 cls: 'table-condensed',
18574                 cn:[
18575                 Roo.bootstrap.DateField.head,
18576                 Roo.bootstrap.DateField.content,
18577                 Roo.bootstrap.DateField.footer
18578                 ]
18579             }
18580             ]
18581         }
18582         ]
18583     }
18584 });
18585
18586  
18587
18588  /*
18589  * - LGPL
18590  *
18591  * TimeField
18592  * 
18593  */
18594
18595 /**
18596  * @class Roo.bootstrap.TimeField
18597  * @extends Roo.bootstrap.Input
18598  * Bootstrap DateField class
18599  * 
18600  * 
18601  * @constructor
18602  * Create a new TimeField
18603  * @param {Object} config The config object
18604  */
18605
18606 Roo.bootstrap.TimeField = function(config){
18607     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18608     this.addEvents({
18609             /**
18610              * @event show
18611              * Fires when this field show.
18612              * @param {Roo.bootstrap.DateField} thisthis
18613              * @param {Mixed} date The date value
18614              */
18615             show : true,
18616             /**
18617              * @event show
18618              * Fires when this field hide.
18619              * @param {Roo.bootstrap.DateField} this
18620              * @param {Mixed} date The date value
18621              */
18622             hide : true,
18623             /**
18624              * @event select
18625              * Fires when select a date.
18626              * @param {Roo.bootstrap.DateField} this
18627              * @param {Mixed} date The date value
18628              */
18629             select : true
18630         });
18631 };
18632
18633 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
18634     
18635     /**
18636      * @cfg {String} format
18637      * The default time format string which can be overriden for localization support.  The format must be
18638      * valid according to {@link Date#parseDate} (defaults to 'H:i').
18639      */
18640     format : "H:i",
18641        
18642     onRender: function(ct, position)
18643     {
18644         
18645         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18646                 
18647         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18648         
18649         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18650         
18651         this.pop = this.picker().select('>.datepicker-time',true).first();
18652         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18653         
18654         this.picker().on('mousedown', this.onMousedown, this);
18655         this.picker().on('click', this.onClick, this);
18656         
18657         this.picker().addClass('datepicker-dropdown');
18658     
18659         this.fillTime();
18660         this.update();
18661             
18662         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18663         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18664         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18665         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18666         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18667         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18668
18669     },
18670     
18671     fireKey: function(e){
18672         if (!this.picker().isVisible()){
18673             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18674                 this.show();
18675             }
18676             return;
18677         }
18678
18679         e.preventDefault();
18680         
18681         switch(e.keyCode){
18682             case 27: // escape
18683                 this.hide();
18684                 break;
18685             case 37: // left
18686             case 39: // right
18687                 this.onTogglePeriod();
18688                 break;
18689             case 38: // up
18690                 this.onIncrementMinutes();
18691                 break;
18692             case 40: // down
18693                 this.onDecrementMinutes();
18694                 break;
18695             case 13: // enter
18696             case 9: // tab
18697                 this.setTime();
18698                 break;
18699         }
18700     },
18701     
18702     onClick: function(e) {
18703         e.stopPropagation();
18704         e.preventDefault();
18705     },
18706     
18707     picker : function()
18708     {
18709         return this.el.select('.datepicker', true).first();
18710     },
18711     
18712     fillTime: function()
18713     {    
18714         var time = this.pop.select('tbody', true).first();
18715         
18716         time.dom.innerHTML = '';
18717         
18718         time.createChild({
18719             tag: 'tr',
18720             cn: [
18721                 {
18722                     tag: 'td',
18723                     cn: [
18724                         {
18725                             tag: 'a',
18726                             href: '#',
18727                             cls: 'btn',
18728                             cn: [
18729                                 {
18730                                     tag: 'span',
18731                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
18732                                 }
18733                             ]
18734                         } 
18735                     ]
18736                 },
18737                 {
18738                     tag: 'td',
18739                     cls: 'separator'
18740                 },
18741                 {
18742                     tag: 'td',
18743                     cn: [
18744                         {
18745                             tag: 'a',
18746                             href: '#',
18747                             cls: 'btn',
18748                             cn: [
18749                                 {
18750                                     tag: 'span',
18751                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
18752                                 }
18753                             ]
18754                         }
18755                     ]
18756                 },
18757                 {
18758                     tag: 'td',
18759                     cls: 'separator'
18760                 }
18761             ]
18762         });
18763         
18764         time.createChild({
18765             tag: 'tr',
18766             cn: [
18767                 {
18768                     tag: 'td',
18769                     cn: [
18770                         {
18771                             tag: 'span',
18772                             cls: 'timepicker-hour',
18773                             html: '00'
18774                         }  
18775                     ]
18776                 },
18777                 {
18778                     tag: 'td',
18779                     cls: 'separator',
18780                     html: ':'
18781                 },
18782                 {
18783                     tag: 'td',
18784                     cn: [
18785                         {
18786                             tag: 'span',
18787                             cls: 'timepicker-minute',
18788                             html: '00'
18789                         }  
18790                     ]
18791                 },
18792                 {
18793                     tag: 'td',
18794                     cls: 'separator'
18795                 },
18796                 {
18797                     tag: 'td',
18798                     cn: [
18799                         {
18800                             tag: 'button',
18801                             type: 'button',
18802                             cls: 'btn btn-primary period',
18803                             html: 'AM'
18804                             
18805                         }
18806                     ]
18807                 }
18808             ]
18809         });
18810         
18811         time.createChild({
18812             tag: 'tr',
18813             cn: [
18814                 {
18815                     tag: 'td',
18816                     cn: [
18817                         {
18818                             tag: 'a',
18819                             href: '#',
18820                             cls: 'btn',
18821                             cn: [
18822                                 {
18823                                     tag: 'span',
18824                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
18825                                 }
18826                             ]
18827                         }
18828                     ]
18829                 },
18830                 {
18831                     tag: 'td',
18832                     cls: 'separator'
18833                 },
18834                 {
18835                     tag: 'td',
18836                     cn: [
18837                         {
18838                             tag: 'a',
18839                             href: '#',
18840                             cls: 'btn',
18841                             cn: [
18842                                 {
18843                                     tag: 'span',
18844                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
18845                                 }
18846                             ]
18847                         }
18848                     ]
18849                 },
18850                 {
18851                     tag: 'td',
18852                     cls: 'separator'
18853                 }
18854             ]
18855         });
18856         
18857     },
18858     
18859     update: function()
18860     {
18861         
18862         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18863         
18864         this.fill();
18865     },
18866     
18867     fill: function() 
18868     {
18869         var hours = this.time.getHours();
18870         var minutes = this.time.getMinutes();
18871         var period = 'AM';
18872         
18873         if(hours > 11){
18874             period = 'PM';
18875         }
18876         
18877         if(hours == 0){
18878             hours = 12;
18879         }
18880         
18881         
18882         if(hours > 12){
18883             hours = hours - 12;
18884         }
18885         
18886         if(hours < 10){
18887             hours = '0' + hours;
18888         }
18889         
18890         if(minutes < 10){
18891             minutes = '0' + minutes;
18892         }
18893         
18894         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18895         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18896         this.pop.select('button', true).first().dom.innerHTML = period;
18897         
18898     },
18899     
18900     place: function()
18901     {   
18902         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18903         
18904         var cls = ['bottom'];
18905         
18906         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18907             cls.pop();
18908             cls.push('top');
18909         }
18910         
18911         cls.push('right');
18912         
18913         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18914             cls.pop();
18915             cls.push('left');
18916         }
18917         
18918         this.picker().addClass(cls.join('-'));
18919         
18920         var _this = this;
18921         
18922         Roo.each(cls, function(c){
18923             if(c == 'bottom'){
18924                 _this.picker().setTop(_this.inputEl().getHeight());
18925                 return;
18926             }
18927             if(c == 'top'){
18928                 _this.picker().setTop(0 - _this.picker().getHeight());
18929                 return;
18930             }
18931             
18932             if(c == 'left'){
18933                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18934                 return;
18935             }
18936             if(c == 'right'){
18937                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18938                 return;
18939             }
18940         });
18941         
18942     },
18943   
18944     onFocus : function()
18945     {
18946         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18947         this.show();
18948     },
18949     
18950     onBlur : function()
18951     {
18952         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18953         this.hide();
18954     },
18955     
18956     show : function()
18957     {
18958         this.picker().show();
18959         this.pop.show();
18960         this.update();
18961         this.place();
18962         
18963         this.fireEvent('show', this, this.date);
18964     },
18965     
18966     hide : function()
18967     {
18968         this.picker().hide();
18969         this.pop.hide();
18970         
18971         this.fireEvent('hide', this, this.date);
18972     },
18973     
18974     setTime : function()
18975     {
18976         this.hide();
18977         this.setValue(this.time.format(this.format));
18978         
18979         this.fireEvent('select', this, this.date);
18980         
18981         
18982     },
18983     
18984     onMousedown: function(e){
18985         e.stopPropagation();
18986         e.preventDefault();
18987     },
18988     
18989     onIncrementHours: function()
18990     {
18991         Roo.log('onIncrementHours');
18992         this.time = this.time.add(Date.HOUR, 1);
18993         this.update();
18994         
18995     },
18996     
18997     onDecrementHours: function()
18998     {
18999         Roo.log('onDecrementHours');
19000         this.time = this.time.add(Date.HOUR, -1);
19001         this.update();
19002     },
19003     
19004     onIncrementMinutes: function()
19005     {
19006         Roo.log('onIncrementMinutes');
19007         this.time = this.time.add(Date.MINUTE, 1);
19008         this.update();
19009     },
19010     
19011     onDecrementMinutes: function()
19012     {
19013         Roo.log('onDecrementMinutes');
19014         this.time = this.time.add(Date.MINUTE, -1);
19015         this.update();
19016     },
19017     
19018     onTogglePeriod: function()
19019     {
19020         Roo.log('onTogglePeriod');
19021         this.time = this.time.add(Date.HOUR, 12);
19022         this.update();
19023     }
19024     
19025    
19026 });
19027
19028 Roo.apply(Roo.bootstrap.TimeField,  {
19029     
19030     content : {
19031         tag: 'tbody',
19032         cn: [
19033             {
19034                 tag: 'tr',
19035                 cn: [
19036                 {
19037                     tag: 'td',
19038                     colspan: '7'
19039                 }
19040                 ]
19041             }
19042         ]
19043     },
19044     
19045     footer : {
19046         tag: 'tfoot',
19047         cn: [
19048             {
19049                 tag: 'tr',
19050                 cn: [
19051                 {
19052                     tag: 'th',
19053                     colspan: '7',
19054                     cls: '',
19055                     cn: [
19056                         {
19057                             tag: 'button',
19058                             cls: 'btn btn-info ok',
19059                             html: 'OK'
19060                         }
19061                     ]
19062                 }
19063
19064                 ]
19065             }
19066         ]
19067     }
19068 });
19069
19070 Roo.apply(Roo.bootstrap.TimeField,  {
19071   
19072     template : {
19073         tag: 'div',
19074         cls: 'datepicker dropdown-menu',
19075         cn: [
19076             {
19077                 tag: 'div',
19078                 cls: 'datepicker-time',
19079                 cn: [
19080                 {
19081                     tag: 'table',
19082                     cls: 'table-condensed',
19083                     cn:[
19084                     Roo.bootstrap.TimeField.content,
19085                     Roo.bootstrap.TimeField.footer
19086                     ]
19087                 }
19088                 ]
19089             }
19090         ]
19091     }
19092 });
19093
19094  
19095
19096  /*
19097  * - LGPL
19098  *
19099  * MonthField
19100  * 
19101  */
19102
19103 /**
19104  * @class Roo.bootstrap.MonthField
19105  * @extends Roo.bootstrap.Input
19106  * Bootstrap MonthField class
19107  * 
19108  * @cfg {String} language default en
19109  * 
19110  * @constructor
19111  * Create a new MonthField
19112  * @param {Object} config The config object
19113  */
19114
19115 Roo.bootstrap.MonthField = function(config){
19116     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19117     
19118     this.addEvents({
19119         /**
19120          * @event show
19121          * Fires when this field show.
19122          * @param {Roo.bootstrap.MonthField} this
19123          * @param {Mixed} date The date value
19124          */
19125         show : true,
19126         /**
19127          * @event show
19128          * Fires when this field hide.
19129          * @param {Roo.bootstrap.MonthField} this
19130          * @param {Mixed} date The date value
19131          */
19132         hide : true,
19133         /**
19134          * @event select
19135          * Fires when select a date.
19136          * @param {Roo.bootstrap.MonthField} this
19137          * @param {String} oldvalue The old value
19138          * @param {String} newvalue The new value
19139          */
19140         select : true
19141     });
19142 };
19143
19144 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19145     
19146     onRender: function(ct, position)
19147     {
19148         
19149         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19150         
19151         this.language = this.language || 'en';
19152         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19153         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19154         
19155         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19156         this.isInline = false;
19157         this.isInput = true;
19158         this.component = this.el.select('.add-on', true).first() || false;
19159         this.component = (this.component && this.component.length === 0) ? false : this.component;
19160         this.hasInput = this.component && this.inputEL().length;
19161         
19162         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19163         
19164         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19165         
19166         this.picker().on('mousedown', this.onMousedown, this);
19167         this.picker().on('click', this.onClick, this);
19168         
19169         this.picker().addClass('datepicker-dropdown');
19170         
19171         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19172             v.setStyle('width', '189px');
19173         });
19174         
19175         this.fillMonths();
19176         
19177         this.update();
19178         
19179         if(this.isInline) {
19180             this.show();
19181         }
19182         
19183     },
19184     
19185     setValue: function(v, suppressEvent)
19186     {   
19187         var o = this.getValue();
19188         
19189         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19190         
19191         this.update();
19192
19193         if(suppressEvent !== true){
19194             this.fireEvent('select', this, o, v);
19195         }
19196         
19197     },
19198     
19199     getValue: function()
19200     {
19201         return this.value;
19202     },
19203     
19204     onClick: function(e) 
19205     {
19206         e.stopPropagation();
19207         e.preventDefault();
19208         
19209         var target = e.getTarget();
19210         
19211         if(target.nodeName.toLowerCase() === 'i'){
19212             target = Roo.get(target).dom.parentNode;
19213         }
19214         
19215         var nodeName = target.nodeName;
19216         var className = target.className;
19217         var html = target.innerHTML;
19218         
19219         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19220             return;
19221         }
19222         
19223         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19224         
19225         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19226         
19227         this.hide();
19228                         
19229     },
19230     
19231     picker : function()
19232     {
19233         return this.pickerEl;
19234     },
19235     
19236     fillMonths: function()
19237     {    
19238         var i = 0;
19239         var months = this.picker().select('>.datepicker-months td', true).first();
19240         
19241         months.dom.innerHTML = '';
19242         
19243         while (i < 12) {
19244             var month = {
19245                 tag: 'span',
19246                 cls: 'month',
19247                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19248             };
19249             
19250             months.createChild(month);
19251         }
19252         
19253     },
19254     
19255     update: function()
19256     {
19257         var _this = this;
19258         
19259         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19260             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19261         }
19262         
19263         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19264             e.removeClass('active');
19265             
19266             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19267                 e.addClass('active');
19268             }
19269         })
19270     },
19271     
19272     place: function()
19273     {
19274         if(this.isInline) {
19275             return;
19276         }
19277         
19278         this.picker().removeClass(['bottom', 'top']);
19279         
19280         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19281             /*
19282              * place to the top of element!
19283              *
19284              */
19285             
19286             this.picker().addClass('top');
19287             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19288             
19289             return;
19290         }
19291         
19292         this.picker().addClass('bottom');
19293         
19294         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19295     },
19296     
19297     onFocus : function()
19298     {
19299         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19300         this.show();
19301     },
19302     
19303     onBlur : function()
19304     {
19305         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19306         
19307         var d = this.inputEl().getValue();
19308         
19309         this.setValue(d);
19310                 
19311         this.hide();
19312     },
19313     
19314     show : function()
19315     {
19316         this.picker().show();
19317         this.picker().select('>.datepicker-months', true).first().show();
19318         this.update();
19319         this.place();
19320         
19321         this.fireEvent('show', this, this.date);
19322     },
19323     
19324     hide : function()
19325     {
19326         if(this.isInline) {
19327             return;
19328         }
19329         this.picker().hide();
19330         this.fireEvent('hide', this, this.date);
19331         
19332     },
19333     
19334     onMousedown: function(e)
19335     {
19336         e.stopPropagation();
19337         e.preventDefault();
19338     },
19339     
19340     keyup: function(e)
19341     {
19342         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19343         this.update();
19344     },
19345
19346     fireKey: function(e)
19347     {
19348         if (!this.picker().isVisible()){
19349             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19350                 this.show();
19351             }
19352             return;
19353         }
19354         
19355         var dir;
19356         
19357         switch(e.keyCode){
19358             case 27: // escape
19359                 this.hide();
19360                 e.preventDefault();
19361                 break;
19362             case 37: // left
19363             case 39: // right
19364                 dir = e.keyCode == 37 ? -1 : 1;
19365                 
19366                 this.vIndex = this.vIndex + dir;
19367                 
19368                 if(this.vIndex < 0){
19369                     this.vIndex = 0;
19370                 }
19371                 
19372                 if(this.vIndex > 11){
19373                     this.vIndex = 11;
19374                 }
19375                 
19376                 if(isNaN(this.vIndex)){
19377                     this.vIndex = 0;
19378                 }
19379                 
19380                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19381                 
19382                 break;
19383             case 38: // up
19384             case 40: // down
19385                 
19386                 dir = e.keyCode == 38 ? -1 : 1;
19387                 
19388                 this.vIndex = this.vIndex + dir * 4;
19389                 
19390                 if(this.vIndex < 0){
19391                     this.vIndex = 0;
19392                 }
19393                 
19394                 if(this.vIndex > 11){
19395                     this.vIndex = 11;
19396                 }
19397                 
19398                 if(isNaN(this.vIndex)){
19399                     this.vIndex = 0;
19400                 }
19401                 
19402                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19403                 break;
19404                 
19405             case 13: // enter
19406                 
19407                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19408                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19409                 }
19410                 
19411                 this.hide();
19412                 e.preventDefault();
19413                 break;
19414             case 9: // tab
19415                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19416                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19417                 }
19418                 this.hide();
19419                 break;
19420             case 16: // shift
19421             case 17: // ctrl
19422             case 18: // alt
19423                 break;
19424             default :
19425                 this.hide();
19426                 
19427         }
19428     },
19429     
19430     remove: function() 
19431     {
19432         this.picker().remove();
19433     }
19434    
19435 });
19436
19437 Roo.apply(Roo.bootstrap.MonthField,  {
19438     
19439     content : {
19440         tag: 'tbody',
19441         cn: [
19442         {
19443             tag: 'tr',
19444             cn: [
19445             {
19446                 tag: 'td',
19447                 colspan: '7'
19448             }
19449             ]
19450         }
19451         ]
19452     },
19453     
19454     dates:{
19455         en: {
19456             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19457             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19458         }
19459     }
19460 });
19461
19462 Roo.apply(Roo.bootstrap.MonthField,  {
19463   
19464     template : {
19465         tag: 'div',
19466         cls: 'datepicker dropdown-menu roo-dynamic',
19467         cn: [
19468             {
19469                 tag: 'div',
19470                 cls: 'datepicker-months',
19471                 cn: [
19472                 {
19473                     tag: 'table',
19474                     cls: 'table-condensed',
19475                     cn:[
19476                         Roo.bootstrap.DateField.content
19477                     ]
19478                 }
19479                 ]
19480             }
19481         ]
19482     }
19483 });
19484
19485  
19486
19487  
19488  /*
19489  * - LGPL
19490  *
19491  * CheckBox
19492  * 
19493  */
19494
19495 /**
19496  * @class Roo.bootstrap.CheckBox
19497  * @extends Roo.bootstrap.Input
19498  * Bootstrap CheckBox class
19499  * 
19500  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19501  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19502  * @cfg {String} boxLabel The text that appears beside the checkbox
19503  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19504  * @cfg {Boolean} checked initnal the element
19505  * @cfg {Boolean} inline inline the element (default false)
19506  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19507  * 
19508  * @constructor
19509  * Create a new CheckBox
19510  * @param {Object} config The config object
19511  */
19512
19513 Roo.bootstrap.CheckBox = function(config){
19514     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19515    
19516     this.addEvents({
19517         /**
19518         * @event check
19519         * Fires when the element is checked or unchecked.
19520         * @param {Roo.bootstrap.CheckBox} this This input
19521         * @param {Boolean} checked The new checked value
19522         */
19523        check : true
19524     });
19525     
19526 };
19527
19528 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
19529   
19530     inputType: 'checkbox',
19531     inputValue: 1,
19532     valueOff: 0,
19533     boxLabel: false,
19534     checked: false,
19535     weight : false,
19536     inline: false,
19537     
19538     getAutoCreate : function()
19539     {
19540         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19541         
19542         var id = Roo.id();
19543         
19544         var cfg = {};
19545         
19546         cfg.cls = 'form-group ' + this.inputType; //input-group
19547         
19548         if(this.inline){
19549             cfg.cls += ' ' + this.inputType + '-inline';
19550         }
19551         
19552         var input =  {
19553             tag: 'input',
19554             id : id,
19555             type : this.inputType,
19556             value : this.inputValue,
19557             cls : 'roo-' + this.inputType, //'form-box',
19558             placeholder : this.placeholder || ''
19559             
19560         };
19561         
19562         if(this.inputType != 'radio'){
19563             var hidden =  {
19564                 tag: 'input',
19565                 type : 'hidden',
19566                 cls : 'roo-hidden-value',
19567                 value : this.checked ? this.valueOff : this.inputValue
19568             };
19569         }
19570         
19571             
19572         if (this.weight) { // Validity check?
19573             cfg.cls += " " + this.inputType + "-" + this.weight;
19574         }
19575         
19576         if (this.disabled) {
19577             input.disabled=true;
19578         }
19579         
19580         if(this.checked){
19581             input.checked = this.checked;
19582             
19583         }
19584         
19585         
19586         if (this.name) {
19587             
19588             input.name = this.name;
19589             
19590             if(this.inputType != 'radio'){
19591                 hidden.name = this.name;
19592                 input.name = '_hidden_' + this.name;
19593             }
19594         }
19595         
19596         if (this.size) {
19597             input.cls += ' input-' + this.size;
19598         }
19599         
19600         var settings=this;
19601         
19602         ['xs','sm','md','lg'].map(function(size){
19603             if (settings[size]) {
19604                 cfg.cls += ' col-' + size + '-' + settings[size];
19605             }
19606         });
19607         
19608         var inputblock = input;
19609          
19610         if (this.before || this.after) {
19611             
19612             inputblock = {
19613                 cls : 'input-group',
19614                 cn :  [] 
19615             };
19616             
19617             if (this.before) {
19618                 inputblock.cn.push({
19619                     tag :'span',
19620                     cls : 'input-group-addon',
19621                     html : this.before
19622                 });
19623             }
19624             
19625             inputblock.cn.push(input);
19626             
19627             if(this.inputType != 'radio'){
19628                 inputblock.cn.push(hidden);
19629             }
19630             
19631             if (this.after) {
19632                 inputblock.cn.push({
19633                     tag :'span',
19634                     cls : 'input-group-addon',
19635                     html : this.after
19636                 });
19637             }
19638             
19639         }
19640         
19641         if (align ==='left' && this.fieldLabel.length) {
19642 //                Roo.log("left and has label");
19643                 cfg.cn = [
19644                     
19645                     {
19646                         tag: 'label',
19647                         'for' :  id,
19648                         cls : 'control-label col-md-' + this.labelWidth,
19649                         html : this.fieldLabel
19650                         
19651                     },
19652                     {
19653                         cls : "col-md-" + (12 - this.labelWidth), 
19654                         cn: [
19655                             inputblock
19656                         ]
19657                     }
19658                     
19659                 ];
19660         } else if ( this.fieldLabel.length) {
19661 //                Roo.log(" label");
19662                 cfg.cn = [
19663                    
19664                     {
19665                         tag: this.boxLabel ? 'span' : 'label',
19666                         'for': id,
19667                         cls: 'control-label box-input-label',
19668                         //cls : 'input-group-addon',
19669                         html : this.fieldLabel
19670                         
19671                     },
19672                     
19673                     inputblock
19674                     
19675                 ];
19676
19677         } else {
19678             
19679 //                Roo.log(" no label && no align");
19680                 cfg.cn = [  inputblock ] ;
19681                 
19682                 
19683         }
19684         
19685         if(this.boxLabel){
19686              var boxLabelCfg = {
19687                 tag: 'label',
19688                 //'for': id, // box label is handled by onclick - so no for...
19689                 cls: 'box-label',
19690                 html: this.boxLabel
19691             };
19692             
19693             if(this.tooltip){
19694                 boxLabelCfg.tooltip = this.tooltip;
19695             }
19696              
19697             cfg.cn.push(boxLabelCfg);
19698         }
19699         
19700         if(this.inputType != 'radio'){
19701             cfg.cn.push(hidden);
19702         }
19703         
19704         return cfg;
19705         
19706     },
19707     
19708     /**
19709      * return the real input element.
19710      */
19711     inputEl: function ()
19712     {
19713         return this.el.select('input.roo-' + this.inputType,true).first();
19714     },
19715     hiddenEl: function ()
19716     {
19717         return this.el.select('input.roo-hidden-value',true).first();
19718     },
19719     
19720     labelEl: function()
19721     {
19722         return this.el.select('label.control-label',true).first();
19723     },
19724     /* depricated... */
19725     
19726     label: function()
19727     {
19728         return this.labelEl();
19729     },
19730     
19731     boxLabelEl: function()
19732     {
19733         return this.el.select('label.box-label',true).first();
19734     },
19735     
19736     initEvents : function()
19737     {
19738 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19739         
19740         this.inputEl().on('click', this.onClick,  this);
19741         
19742         if (this.boxLabel) { 
19743             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
19744         }
19745         
19746         this.startValue = this.getValue();
19747         
19748         if(this.groupId){
19749             Roo.bootstrap.CheckBox.register(this);
19750         }
19751     },
19752     
19753     onClick : function()
19754     {   
19755         this.setChecked(!this.checked);
19756     },
19757     
19758     setChecked : function(state,suppressEvent)
19759     {
19760         this.startValue = this.getValue();
19761         
19762         if(this.inputType == 'radio'){
19763             
19764             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19765                 e.dom.checked = false;
19766             });
19767             
19768             this.inputEl().dom.checked = true;
19769             
19770             this.inputEl().dom.value = this.inputValue;
19771             
19772             if(suppressEvent !== true){
19773                 this.fireEvent('check', this, true);
19774             }
19775             
19776             this.validate();
19777             
19778             return;
19779         }
19780         
19781         this.checked = state;
19782         
19783         this.inputEl().dom.checked = state;
19784         
19785         
19786         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
19787         
19788         if(suppressEvent !== true){
19789             this.fireEvent('check', this, state);
19790         }
19791         
19792         this.validate();
19793     },
19794     
19795     getValue : function()
19796     {
19797         if(this.inputType == 'radio'){
19798             return this.getGroupValue();
19799         }
19800         
19801         return this.hiddenEl().dom.value;
19802         
19803     },
19804     
19805     getGroupValue : function()
19806     {
19807         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19808             return '';
19809         }
19810         
19811         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19812     },
19813     
19814     setValue : function(v,suppressEvent)
19815     {
19816         if(this.inputType == 'radio'){
19817             this.setGroupValue(v, suppressEvent);
19818             return;
19819         }
19820         
19821         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19822         
19823         this.validate();
19824     },
19825     
19826     setGroupValue : function(v, suppressEvent)
19827     {
19828         this.startValue = this.getValue();
19829         
19830         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19831             e.dom.checked = false;
19832             
19833             if(e.dom.value == v){
19834                 e.dom.checked = true;
19835             }
19836         });
19837         
19838         if(suppressEvent !== true){
19839             this.fireEvent('check', this, true);
19840         }
19841
19842         this.validate();
19843         
19844         return;
19845     },
19846     
19847     validate : function()
19848     {
19849         if(
19850                 this.disabled || 
19851                 (this.inputType == 'radio' && this.validateRadio()) ||
19852                 (this.inputType == 'checkbox' && this.validateCheckbox())
19853         ){
19854             this.markValid();
19855             return true;
19856         }
19857         
19858         this.markInvalid();
19859         return false;
19860     },
19861     
19862     validateRadio : function()
19863     {
19864         if(this.allowBlank){
19865             return true;
19866         }
19867         
19868         var valid = false;
19869         
19870         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19871             if(!e.dom.checked){
19872                 return;
19873             }
19874             
19875             valid = true;
19876             
19877             return false;
19878         });
19879         
19880         return valid;
19881     },
19882     
19883     validateCheckbox : function()
19884     {
19885         if(!this.groupId){
19886             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19887         }
19888         
19889         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19890         
19891         if(!group){
19892             return false;
19893         }
19894         
19895         var r = false;
19896         
19897         for(var i in group){
19898             if(r){
19899                 break;
19900             }
19901             
19902             r = (group[i].getValue() == group[i].inputValue) ? true : false;
19903         }
19904         
19905         return r;
19906     },
19907     
19908     /**
19909      * Mark this field as valid
19910      */
19911     markValid : function()
19912     {
19913         if(this.allowBlank){
19914             return;
19915         }
19916         
19917         var _this = this;
19918         
19919         this.fireEvent('valid', this);
19920         
19921         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19922         
19923         if(this.groupId){
19924             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19925         }
19926         
19927         if(label){
19928             label.markValid();
19929         }
19930
19931         if(this.inputType == 'radio'){
19932             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19933                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19934                 e.findParent('.form-group', false, true).addClass(_this.validClass);
19935             });
19936             
19937             return;
19938         }
19939         
19940         if(!this.groupId){
19941             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19942             this.el.findParent('.form-group', false, true).addClass(this.validClass);
19943             return;
19944         }
19945         
19946         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19947             
19948         if(!group){
19949             return;
19950         }
19951         
19952         for(var i in group){
19953             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19954             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19955         }
19956     },
19957     
19958      /**
19959      * Mark this field as invalid
19960      * @param {String} msg The validation message
19961      */
19962     markInvalid : function(msg)
19963     {
19964         if(this.allowBlank){
19965             return;
19966         }
19967         
19968         var _this = this;
19969         
19970         this.fireEvent('invalid', this, msg);
19971         
19972         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19973         
19974         if(this.groupId){
19975             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19976         }
19977         
19978         if(label){
19979             label.markInvalid();
19980         }
19981             
19982         if(this.inputType == 'radio'){
19983             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19984                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19985                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19986             });
19987             
19988             return;
19989         }
19990         
19991         if(!this.groupId){
19992             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19993             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19994             return;
19995         }
19996         
19997         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19998         
19999         if(!group){
20000             return;
20001         }
20002         
20003         for(var i in group){
20004             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20005             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20006         }
20007         
20008     },
20009     
20010     disable : function()
20011     {
20012         if(this.inputType != 'radio'){
20013             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20014             return;
20015         }
20016         
20017         var _this = this;
20018         
20019         if(this.rendered){
20020             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20021                 _this.getActionEl().addClass(this.disabledClass);
20022                 e.dom.disabled = true;
20023             });
20024         }
20025         
20026         this.disabled = true;
20027         this.fireEvent("disable", this);
20028         return this;
20029     },
20030
20031     enable : function()
20032     {
20033         if(this.inputType != 'radio'){
20034             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20035             return;
20036         }
20037         
20038         var _this = this;
20039         
20040         if(this.rendered){
20041             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20042                 _this.getActionEl().removeClass(this.disabledClass);
20043                 e.dom.disabled = false;
20044             });
20045         }
20046         
20047         this.disabled = false;
20048         this.fireEvent("enable", this);
20049         return this;
20050     }
20051
20052 });
20053
20054 Roo.apply(Roo.bootstrap.CheckBox, {
20055     
20056     groups: {},
20057     
20058      /**
20059     * register a CheckBox Group
20060     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20061     */
20062     register : function(checkbox)
20063     {
20064         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20065             this.groups[checkbox.groupId] = {};
20066         }
20067         
20068         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20069             return;
20070         }
20071         
20072         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20073         
20074     },
20075     /**
20076     * fetch a CheckBox Group based on the group ID
20077     * @param {string} the group ID
20078     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20079     */
20080     get: function(groupId) {
20081         if (typeof(this.groups[groupId]) == 'undefined') {
20082             return false;
20083         }
20084         
20085         return this.groups[groupId] ;
20086     }
20087     
20088     
20089 });
20090 /*
20091  * - LGPL
20092  *
20093  * Radio
20094  *
20095  *
20096  * not inline
20097  *<div class="radio">
20098   <label>
20099     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
20100     Option one is this and that&mdash;be sure to include why it's great
20101   </label>
20102 </div>
20103  *
20104  *
20105  *inline
20106  *<span>
20107  *<label class="radio-inline">fieldLabel</label>
20108  *<label class="radio-inline">
20109   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
20110 </label>
20111 <span>
20112  *
20113  *
20114  */
20115
20116 /**
20117  * @class Roo.bootstrap.Radio
20118  * @extends Roo.bootstrap.CheckBox
20119  * Bootstrap Radio class
20120
20121  * @constructor
20122  * Create a new Radio
20123  * @param {Object} config The config object
20124  */
20125
20126 Roo.bootstrap.Radio = function(config){
20127     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20128
20129 };
20130
20131 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
20132
20133     inputType: 'radio',
20134     inputValue: '',
20135     valueOff: '',
20136
20137     getAutoCreate : function()
20138     {
20139         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20140         align = align || 'left'; // default...
20141
20142
20143
20144         var id = Roo.id();
20145
20146         var cfg = {
20147                 tag : this.inline ? 'span' : 'div',
20148                 cls : 'form-group',
20149                 cn : []
20150         };
20151
20152         var inline = this.inline ? ' radio-inline' : '';
20153
20154         var lbl = {
20155                 tag: 'label' ,
20156                 // does not need for, as we wrap the input with it..
20157                 'for' : id,
20158                 cls : 'control-label box-label' + inline,
20159                 cn : []
20160         };
20161         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
20162
20163         var fieldLabel = {
20164             tag: 'label' ,
20165             //cls : 'control-label' + inline,
20166             html : this.fieldLabel,
20167             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
20168         };
20169
20170         var input =  {
20171             tag: 'input',
20172             id : id,
20173             type : this.inputType,
20174             //value : (!this.checked) ? this.valueOff : this.inputValue,
20175             value : this.inputValue,
20176             cls : 'roo-radio',
20177             placeholder : this.placeholder || '' // ?? needed????
20178
20179         };
20180         if (this.weight) { // Validity check?
20181             input.cls += " radio-" + this.weight;
20182         }
20183         if (this.disabled) {
20184             input.disabled=true;
20185         }
20186
20187         if(this.checked){
20188             input.checked = this.checked;
20189         }
20190
20191         if (this.name) {
20192             input.name = this.name;
20193         }
20194
20195         if (this.size) {
20196             input.cls += ' input-' + this.size;
20197         }
20198
20199         //?? can span's inline have a width??
20200
20201         var settings=this;
20202         ['xs','sm','md','lg'].map(function(size){
20203             if (settings[size]) {
20204                 cfg.cls += ' col-' + size + '-' + settings[size];
20205             }
20206         });
20207
20208         var inputblock = input;
20209
20210         if (this.before || this.after) {
20211
20212             inputblock = {
20213                 cls : 'input-group',
20214                 tag : 'span',
20215                 cn :  []
20216             };
20217             if (this.before) {
20218                 inputblock.cn.push({
20219                     tag :'span',
20220                     cls : 'input-group-addon',
20221                     html : this.before
20222                 });
20223             }
20224             inputblock.cn.push(input);
20225             if (this.after) {
20226                 inputblock.cn.push({
20227                     tag :'span',
20228                     cls : 'input-group-addon',
20229                     html : this.after
20230                 });
20231             }
20232
20233         };
20234
20235
20236         if (this.fieldLabel && this.fieldLabel.length) {
20237             cfg.cn.push(fieldLabel);
20238         }
20239
20240         // normal bootstrap puts the input inside the label.
20241         // however with our styled version - it has to go after the input.
20242
20243         //lbl.cn.push(inputblock);
20244
20245         var lblwrap =  {
20246             tag: 'span',
20247             cls: 'radio' + inline,
20248             cn: [
20249                 inputblock,
20250                 lbl
20251             ]
20252         };
20253
20254         cfg.cn.push( lblwrap);
20255
20256         if(this.boxLabel){
20257             lbl.cn.push({
20258                 tag: 'span',
20259                 html: this.boxLabel
20260             })
20261         }
20262
20263
20264         return cfg;
20265
20266     },
20267
20268     initEvents : function()
20269     {
20270 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20271
20272         this.inputEl().on('click', this.onClick,  this);
20273         if (this.boxLabel) {
20274             //Roo.log('find label');
20275             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
20276         }
20277
20278     },
20279
20280     inputEl: function ()
20281     {
20282         return this.el.select('input.roo-radio',true).first();
20283     },
20284     onClick : function()
20285     {
20286         Roo.log("click");
20287         this.setChecked(true);
20288     },
20289
20290     setChecked : function(state,suppressEvent)
20291     {
20292         if(state){
20293             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20294                 v.dom.checked = false;
20295             });
20296         }
20297         Roo.log(this.inputEl().dom);
20298         this.checked = state;
20299         this.inputEl().dom.checked = state;
20300
20301         if(suppressEvent !== true){
20302             this.fireEvent('check', this, state);
20303         }
20304
20305         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
20306
20307     },
20308
20309     getGroupValue : function()
20310     {
20311         var value = '';
20312         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20313             if(v.dom.checked == true){
20314                 value = v.dom.value;
20315             }
20316         });
20317
20318         return value;
20319     },
20320
20321     /**
20322      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
20323      * @return {Mixed} value The field value
20324      */
20325     getValue : function(){
20326         return this.getGroupValue();
20327     }
20328
20329 });
20330 //<script type="text/javascript">
20331
20332 /*
20333  * Based  Ext JS Library 1.1.1
20334  * Copyright(c) 2006-2007, Ext JS, LLC.
20335  * LGPL
20336  *
20337  */
20338  
20339 /**
20340  * @class Roo.HtmlEditorCore
20341  * @extends Roo.Component
20342  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20343  *
20344  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20345  */
20346
20347 Roo.HtmlEditorCore = function(config){
20348     
20349     
20350     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20351     
20352     
20353     this.addEvents({
20354         /**
20355          * @event initialize
20356          * Fires when the editor is fully initialized (including the iframe)
20357          * @param {Roo.HtmlEditorCore} this
20358          */
20359         initialize: true,
20360         /**
20361          * @event activate
20362          * Fires when the editor is first receives the focus. Any insertion must wait
20363          * until after this event.
20364          * @param {Roo.HtmlEditorCore} this
20365          */
20366         activate: true,
20367          /**
20368          * @event beforesync
20369          * Fires before the textarea is updated with content from the editor iframe. Return false
20370          * to cancel the sync.
20371          * @param {Roo.HtmlEditorCore} this
20372          * @param {String} html
20373          */
20374         beforesync: true,
20375          /**
20376          * @event beforepush
20377          * Fires before the iframe editor is updated with content from the textarea. Return false
20378          * to cancel the push.
20379          * @param {Roo.HtmlEditorCore} this
20380          * @param {String} html
20381          */
20382         beforepush: true,
20383          /**
20384          * @event sync
20385          * Fires when the textarea is updated with content from the editor iframe.
20386          * @param {Roo.HtmlEditorCore} this
20387          * @param {String} html
20388          */
20389         sync: true,
20390          /**
20391          * @event push
20392          * Fires when the iframe editor is updated with content from the textarea.
20393          * @param {Roo.HtmlEditorCore} this
20394          * @param {String} html
20395          */
20396         push: true,
20397         
20398         /**
20399          * @event editorevent
20400          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20401          * @param {Roo.HtmlEditorCore} this
20402          */
20403         editorevent: true
20404         
20405     });
20406     
20407     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20408     
20409     // defaults : white / black...
20410     this.applyBlacklists();
20411     
20412     
20413     
20414 };
20415
20416
20417 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20418
20419
20420      /**
20421      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20422      */
20423     
20424     owner : false,
20425     
20426      /**
20427      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20428      *                        Roo.resizable.
20429      */
20430     resizable : false,
20431      /**
20432      * @cfg {Number} height (in pixels)
20433      */   
20434     height: 300,
20435    /**
20436      * @cfg {Number} width (in pixels)
20437      */   
20438     width: 500,
20439     
20440     /**
20441      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20442      * 
20443      */
20444     stylesheets: false,
20445     
20446     // id of frame..
20447     frameId: false,
20448     
20449     // private properties
20450     validationEvent : false,
20451     deferHeight: true,
20452     initialized : false,
20453     activated : false,
20454     sourceEditMode : false,
20455     onFocus : Roo.emptyFn,
20456     iframePad:3,
20457     hideMode:'offsets',
20458     
20459     clearUp: true,
20460     
20461     // blacklist + whitelisted elements..
20462     black: false,
20463     white: false,
20464      
20465     
20466
20467     /**
20468      * Protected method that will not generally be called directly. It
20469      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20470      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20471      */
20472     getDocMarkup : function(){
20473         // body styles..
20474         var st = '';
20475         
20476         // inherit styels from page...?? 
20477         if (this.stylesheets === false) {
20478             
20479             Roo.get(document.head).select('style').each(function(node) {
20480                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20481             });
20482             
20483             Roo.get(document.head).select('link').each(function(node) { 
20484                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20485             });
20486             
20487         } else if (!this.stylesheets.length) {
20488                 // simple..
20489                 st = '<style type="text/css">' +
20490                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20491                    '</style>';
20492         } else { 
20493             
20494         }
20495         
20496         st +=  '<style type="text/css">' +
20497             'IMG { cursor: pointer } ' +
20498         '</style>';
20499
20500         
20501         return '<html><head>' + st  +
20502             //<style type="text/css">' +
20503             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20504             //'</style>' +
20505             ' </head><body class="roo-htmleditor-body"></body></html>';
20506     },
20507
20508     // private
20509     onRender : function(ct, position)
20510     {
20511         var _t = this;
20512         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20513         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20514         
20515         
20516         this.el.dom.style.border = '0 none';
20517         this.el.dom.setAttribute('tabIndex', -1);
20518         this.el.addClass('x-hidden hide');
20519         
20520         
20521         
20522         if(Roo.isIE){ // fix IE 1px bogus margin
20523             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20524         }
20525        
20526         
20527         this.frameId = Roo.id();
20528         
20529          
20530         
20531         var iframe = this.owner.wrap.createChild({
20532             tag: 'iframe',
20533             cls: 'form-control', // bootstrap..
20534             id: this.frameId,
20535             name: this.frameId,
20536             frameBorder : 'no',
20537             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20538         }, this.el
20539         );
20540         
20541         
20542         this.iframe = iframe.dom;
20543
20544          this.assignDocWin();
20545         
20546         this.doc.designMode = 'on';
20547        
20548         this.doc.open();
20549         this.doc.write(this.getDocMarkup());
20550         this.doc.close();
20551
20552         
20553         var task = { // must defer to wait for browser to be ready
20554             run : function(){
20555                 //console.log("run task?" + this.doc.readyState);
20556                 this.assignDocWin();
20557                 if(this.doc.body || this.doc.readyState == 'complete'){
20558                     try {
20559                         this.doc.designMode="on";
20560                     } catch (e) {
20561                         return;
20562                     }
20563                     Roo.TaskMgr.stop(task);
20564                     this.initEditor.defer(10, this);
20565                 }
20566             },
20567             interval : 10,
20568             duration: 10000,
20569             scope: this
20570         };
20571         Roo.TaskMgr.start(task);
20572
20573     },
20574
20575     // private
20576     onResize : function(w, h)
20577     {
20578          Roo.log('resize: ' +w + ',' + h );
20579         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20580         if(!this.iframe){
20581             return;
20582         }
20583         if(typeof w == 'number'){
20584             
20585             this.iframe.style.width = w + 'px';
20586         }
20587         if(typeof h == 'number'){
20588             
20589             this.iframe.style.height = h + 'px';
20590             if(this.doc){
20591                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20592             }
20593         }
20594         
20595     },
20596
20597     /**
20598      * Toggles the editor between standard and source edit mode.
20599      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20600      */
20601     toggleSourceEdit : function(sourceEditMode){
20602         
20603         this.sourceEditMode = sourceEditMode === true;
20604         
20605         if(this.sourceEditMode){
20606  
20607             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20608             
20609         }else{
20610             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20611             //this.iframe.className = '';
20612             this.deferFocus();
20613         }
20614         //this.setSize(this.owner.wrap.getSize());
20615         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20616     },
20617
20618     
20619   
20620
20621     /**
20622      * Protected method that will not generally be called directly. If you need/want
20623      * custom HTML cleanup, this is the method you should override.
20624      * @param {String} html The HTML to be cleaned
20625      * return {String} The cleaned HTML
20626      */
20627     cleanHtml : function(html){
20628         html = String(html);
20629         if(html.length > 5){
20630             if(Roo.isSafari){ // strip safari nonsense
20631                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20632             }
20633         }
20634         if(html == '&nbsp;'){
20635             html = '';
20636         }
20637         return html;
20638     },
20639
20640     /**
20641      * HTML Editor -> Textarea
20642      * Protected method that will not generally be called directly. Syncs the contents
20643      * of the editor iframe with the textarea.
20644      */
20645     syncValue : function(){
20646         if(this.initialized){
20647             var bd = (this.doc.body || this.doc.documentElement);
20648             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20649             var html = bd.innerHTML;
20650             if(Roo.isSafari){
20651                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20652                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20653                 if(m && m[1]){
20654                     html = '<div style="'+m[0]+'">' + html + '</div>';
20655                 }
20656             }
20657             html = this.cleanHtml(html);
20658             // fix up the special chars.. normaly like back quotes in word...
20659             // however we do not want to do this with chinese..
20660             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20661                 var cc = b.charCodeAt();
20662                 if (
20663                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20664                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20665                     (cc >= 0xf900 && cc < 0xfb00 )
20666                 ) {
20667                         return b;
20668                 }
20669                 return "&#"+cc+";" 
20670             });
20671             if(this.owner.fireEvent('beforesync', this, html) !== false){
20672                 this.el.dom.value = html;
20673                 this.owner.fireEvent('sync', this, html);
20674             }
20675         }
20676     },
20677
20678     /**
20679      * Protected method that will not generally be called directly. Pushes the value of the textarea
20680      * into the iframe editor.
20681      */
20682     pushValue : function(){
20683         if(this.initialized){
20684             var v = this.el.dom.value.trim();
20685             
20686 //            if(v.length < 1){
20687 //                v = '&#160;';
20688 //            }
20689             
20690             if(this.owner.fireEvent('beforepush', this, v) !== false){
20691                 var d = (this.doc.body || this.doc.documentElement);
20692                 d.innerHTML = v;
20693                 this.cleanUpPaste();
20694                 this.el.dom.value = d.innerHTML;
20695                 this.owner.fireEvent('push', this, v);
20696             }
20697         }
20698     },
20699
20700     // private
20701     deferFocus : function(){
20702         this.focus.defer(10, this);
20703     },
20704
20705     // doc'ed in Field
20706     focus : function(){
20707         if(this.win && !this.sourceEditMode){
20708             this.win.focus();
20709         }else{
20710             this.el.focus();
20711         }
20712     },
20713     
20714     assignDocWin: function()
20715     {
20716         var iframe = this.iframe;
20717         
20718          if(Roo.isIE){
20719             this.doc = iframe.contentWindow.document;
20720             this.win = iframe.contentWindow;
20721         } else {
20722 //            if (!Roo.get(this.frameId)) {
20723 //                return;
20724 //            }
20725 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20726 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20727             
20728             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20729                 return;
20730             }
20731             
20732             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20733             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20734         }
20735     },
20736     
20737     // private
20738     initEditor : function(){
20739         //console.log("INIT EDITOR");
20740         this.assignDocWin();
20741         
20742         
20743         
20744         this.doc.designMode="on";
20745         this.doc.open();
20746         this.doc.write(this.getDocMarkup());
20747         this.doc.close();
20748         
20749         var dbody = (this.doc.body || this.doc.documentElement);
20750         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20751         // this copies styles from the containing element into thsi one..
20752         // not sure why we need all of this..
20753         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20754         
20755         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20756         //ss['background-attachment'] = 'fixed'; // w3c
20757         dbody.bgProperties = 'fixed'; // ie
20758         //Roo.DomHelper.applyStyles(dbody, ss);
20759         Roo.EventManager.on(this.doc, {
20760             //'mousedown': this.onEditorEvent,
20761             'mouseup': this.onEditorEvent,
20762             'dblclick': this.onEditorEvent,
20763             'click': this.onEditorEvent,
20764             'keyup': this.onEditorEvent,
20765             buffer:100,
20766             scope: this
20767         });
20768         if(Roo.isGecko){
20769             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20770         }
20771         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20772             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20773         }
20774         this.initialized = true;
20775
20776         this.owner.fireEvent('initialize', this);
20777         this.pushValue();
20778     },
20779
20780     // private
20781     onDestroy : function(){
20782         
20783         
20784         
20785         if(this.rendered){
20786             
20787             //for (var i =0; i < this.toolbars.length;i++) {
20788             //    // fixme - ask toolbars for heights?
20789             //    this.toolbars[i].onDestroy();
20790            // }
20791             
20792             //this.wrap.dom.innerHTML = '';
20793             //this.wrap.remove();
20794         }
20795     },
20796
20797     // private
20798     onFirstFocus : function(){
20799         
20800         this.assignDocWin();
20801         
20802         
20803         this.activated = true;
20804          
20805     
20806         if(Roo.isGecko){ // prevent silly gecko errors
20807             this.win.focus();
20808             var s = this.win.getSelection();
20809             if(!s.focusNode || s.focusNode.nodeType != 3){
20810                 var r = s.getRangeAt(0);
20811                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20812                 r.collapse(true);
20813                 this.deferFocus();
20814             }
20815             try{
20816                 this.execCmd('useCSS', true);
20817                 this.execCmd('styleWithCSS', false);
20818             }catch(e){}
20819         }
20820         this.owner.fireEvent('activate', this);
20821     },
20822
20823     // private
20824     adjustFont: function(btn){
20825         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20826         //if(Roo.isSafari){ // safari
20827         //    adjust *= 2;
20828        // }
20829         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20830         if(Roo.isSafari){ // safari
20831             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20832             v =  (v < 10) ? 10 : v;
20833             v =  (v > 48) ? 48 : v;
20834             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20835             
20836         }
20837         
20838         
20839         v = Math.max(1, v+adjust);
20840         
20841         this.execCmd('FontSize', v  );
20842     },
20843
20844     onEditorEvent : function(e)
20845     {
20846         this.owner.fireEvent('editorevent', this, e);
20847       //  this.updateToolbar();
20848         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20849     },
20850
20851     insertTag : function(tg)
20852     {
20853         // could be a bit smarter... -> wrap the current selected tRoo..
20854         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20855             
20856             range = this.createRange(this.getSelection());
20857             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20858             wrappingNode.appendChild(range.extractContents());
20859             range.insertNode(wrappingNode);
20860
20861             return;
20862             
20863             
20864             
20865         }
20866         this.execCmd("formatblock",   tg);
20867         
20868     },
20869     
20870     insertText : function(txt)
20871     {
20872         
20873         
20874         var range = this.createRange();
20875         range.deleteContents();
20876                //alert(Sender.getAttribute('label'));
20877                
20878         range.insertNode(this.doc.createTextNode(txt));
20879     } ,
20880     
20881      
20882
20883     /**
20884      * Executes a Midas editor command on the editor document and performs necessary focus and
20885      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20886      * @param {String} cmd The Midas command
20887      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20888      */
20889     relayCmd : function(cmd, value){
20890         this.win.focus();
20891         this.execCmd(cmd, value);
20892         this.owner.fireEvent('editorevent', this);
20893         //this.updateToolbar();
20894         this.owner.deferFocus();
20895     },
20896
20897     /**
20898      * Executes a Midas editor command directly on the editor document.
20899      * For visual commands, you should use {@link #relayCmd} instead.
20900      * <b>This should only be called after the editor is initialized.</b>
20901      * @param {String} cmd The Midas command
20902      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20903      */
20904     execCmd : function(cmd, value){
20905         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20906         this.syncValue();
20907     },
20908  
20909  
20910    
20911     /**
20912      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20913      * to insert tRoo.
20914      * @param {String} text | dom node.. 
20915      */
20916     insertAtCursor : function(text)
20917     {
20918         
20919         
20920         
20921         if(!this.activated){
20922             return;
20923         }
20924         /*
20925         if(Roo.isIE){
20926             this.win.focus();
20927             var r = this.doc.selection.createRange();
20928             if(r){
20929                 r.collapse(true);
20930                 r.pasteHTML(text);
20931                 this.syncValue();
20932                 this.deferFocus();
20933             
20934             }
20935             return;
20936         }
20937         */
20938         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20939             this.win.focus();
20940             
20941             
20942             // from jquery ui (MIT licenced)
20943             var range, node;
20944             var win = this.win;
20945             
20946             if (win.getSelection && win.getSelection().getRangeAt) {
20947                 range = win.getSelection().getRangeAt(0);
20948                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20949                 range.insertNode(node);
20950             } else if (win.document.selection && win.document.selection.createRange) {
20951                 // no firefox support
20952                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20953                 win.document.selection.createRange().pasteHTML(txt);
20954             } else {
20955                 // no firefox support
20956                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20957                 this.execCmd('InsertHTML', txt);
20958             } 
20959             
20960             this.syncValue();
20961             
20962             this.deferFocus();
20963         }
20964     },
20965  // private
20966     mozKeyPress : function(e){
20967         if(e.ctrlKey){
20968             var c = e.getCharCode(), cmd;
20969           
20970             if(c > 0){
20971                 c = String.fromCharCode(c).toLowerCase();
20972                 switch(c){
20973                     case 'b':
20974                         cmd = 'bold';
20975                         break;
20976                     case 'i':
20977                         cmd = 'italic';
20978                         break;
20979                     
20980                     case 'u':
20981                         cmd = 'underline';
20982                         break;
20983                     
20984                     case 'v':
20985                         this.cleanUpPaste.defer(100, this);
20986                         return;
20987                         
20988                 }
20989                 if(cmd){
20990                     this.win.focus();
20991                     this.execCmd(cmd);
20992                     this.deferFocus();
20993                     e.preventDefault();
20994                 }
20995                 
20996             }
20997         }
20998     },
20999
21000     // private
21001     fixKeys : function(){ // load time branching for fastest keydown performance
21002         if(Roo.isIE){
21003             return function(e){
21004                 var k = e.getKey(), r;
21005                 if(k == e.TAB){
21006                     e.stopEvent();
21007                     r = this.doc.selection.createRange();
21008                     if(r){
21009                         r.collapse(true);
21010                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21011                         this.deferFocus();
21012                     }
21013                     return;
21014                 }
21015                 
21016                 if(k == e.ENTER){
21017                     r = this.doc.selection.createRange();
21018                     if(r){
21019                         var target = r.parentElement();
21020                         if(!target || target.tagName.toLowerCase() != 'li'){
21021                             e.stopEvent();
21022                             r.pasteHTML('<br />');
21023                             r.collapse(false);
21024                             r.select();
21025                         }
21026                     }
21027                 }
21028                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21029                     this.cleanUpPaste.defer(100, this);
21030                     return;
21031                 }
21032                 
21033                 
21034             };
21035         }else if(Roo.isOpera){
21036             return function(e){
21037                 var k = e.getKey();
21038                 if(k == e.TAB){
21039                     e.stopEvent();
21040                     this.win.focus();
21041                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21042                     this.deferFocus();
21043                 }
21044                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21045                     this.cleanUpPaste.defer(100, this);
21046                     return;
21047                 }
21048                 
21049             };
21050         }else if(Roo.isSafari){
21051             return function(e){
21052                 var k = e.getKey();
21053                 
21054                 if(k == e.TAB){
21055                     e.stopEvent();
21056                     this.execCmd('InsertText','\t');
21057                     this.deferFocus();
21058                     return;
21059                 }
21060                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21061                     this.cleanUpPaste.defer(100, this);
21062                     return;
21063                 }
21064                 
21065              };
21066         }
21067     }(),
21068     
21069     getAllAncestors: function()
21070     {
21071         var p = this.getSelectedNode();
21072         var a = [];
21073         if (!p) {
21074             a.push(p); // push blank onto stack..
21075             p = this.getParentElement();
21076         }
21077         
21078         
21079         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21080             a.push(p);
21081             p = p.parentNode;
21082         }
21083         a.push(this.doc.body);
21084         return a;
21085     },
21086     lastSel : false,
21087     lastSelNode : false,
21088     
21089     
21090     getSelection : function() 
21091     {
21092         this.assignDocWin();
21093         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21094     },
21095     
21096     getSelectedNode: function() 
21097     {
21098         // this may only work on Gecko!!!
21099         
21100         // should we cache this!!!!
21101         
21102         
21103         
21104          
21105         var range = this.createRange(this.getSelection()).cloneRange();
21106         
21107         if (Roo.isIE) {
21108             var parent = range.parentElement();
21109             while (true) {
21110                 var testRange = range.duplicate();
21111                 testRange.moveToElementText(parent);
21112                 if (testRange.inRange(range)) {
21113                     break;
21114                 }
21115                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21116                     break;
21117                 }
21118                 parent = parent.parentElement;
21119             }
21120             return parent;
21121         }
21122         
21123         // is ancestor a text element.
21124         var ac =  range.commonAncestorContainer;
21125         if (ac.nodeType == 3) {
21126             ac = ac.parentNode;
21127         }
21128         
21129         var ar = ac.childNodes;
21130          
21131         var nodes = [];
21132         var other_nodes = [];
21133         var has_other_nodes = false;
21134         for (var i=0;i<ar.length;i++) {
21135             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21136                 continue;
21137             }
21138             // fullly contained node.
21139             
21140             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21141                 nodes.push(ar[i]);
21142                 continue;
21143             }
21144             
21145             // probably selected..
21146             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21147                 other_nodes.push(ar[i]);
21148                 continue;
21149             }
21150             // outer..
21151             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21152                 continue;
21153             }
21154             
21155             
21156             has_other_nodes = true;
21157         }
21158         if (!nodes.length && other_nodes.length) {
21159             nodes= other_nodes;
21160         }
21161         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21162             return false;
21163         }
21164         
21165         return nodes[0];
21166     },
21167     createRange: function(sel)
21168     {
21169         // this has strange effects when using with 
21170         // top toolbar - not sure if it's a great idea.
21171         //this.editor.contentWindow.focus();
21172         if (typeof sel != "undefined") {
21173             try {
21174                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21175             } catch(e) {
21176                 return this.doc.createRange();
21177             }
21178         } else {
21179             return this.doc.createRange();
21180         }
21181     },
21182     getParentElement: function()
21183     {
21184         
21185         this.assignDocWin();
21186         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21187         
21188         var range = this.createRange(sel);
21189          
21190         try {
21191             var p = range.commonAncestorContainer;
21192             while (p.nodeType == 3) { // text node
21193                 p = p.parentNode;
21194             }
21195             return p;
21196         } catch (e) {
21197             return null;
21198         }
21199     
21200     },
21201     /***
21202      *
21203      * Range intersection.. the hard stuff...
21204      *  '-1' = before
21205      *  '0' = hits..
21206      *  '1' = after.
21207      *         [ -- selected range --- ]
21208      *   [fail]                        [fail]
21209      *
21210      *    basically..
21211      *      if end is before start or  hits it. fail.
21212      *      if start is after end or hits it fail.
21213      *
21214      *   if either hits (but other is outside. - then it's not 
21215      *   
21216      *    
21217      **/
21218     
21219     
21220     // @see http://www.thismuchiknow.co.uk/?p=64.
21221     rangeIntersectsNode : function(range, node)
21222     {
21223         var nodeRange = node.ownerDocument.createRange();
21224         try {
21225             nodeRange.selectNode(node);
21226         } catch (e) {
21227             nodeRange.selectNodeContents(node);
21228         }
21229     
21230         var rangeStartRange = range.cloneRange();
21231         rangeStartRange.collapse(true);
21232     
21233         var rangeEndRange = range.cloneRange();
21234         rangeEndRange.collapse(false);
21235     
21236         var nodeStartRange = nodeRange.cloneRange();
21237         nodeStartRange.collapse(true);
21238     
21239         var nodeEndRange = nodeRange.cloneRange();
21240         nodeEndRange.collapse(false);
21241     
21242         return rangeStartRange.compareBoundaryPoints(
21243                  Range.START_TO_START, nodeEndRange) == -1 &&
21244                rangeEndRange.compareBoundaryPoints(
21245                  Range.START_TO_START, nodeStartRange) == 1;
21246         
21247          
21248     },
21249     rangeCompareNode : function(range, node)
21250     {
21251         var nodeRange = node.ownerDocument.createRange();
21252         try {
21253             nodeRange.selectNode(node);
21254         } catch (e) {
21255             nodeRange.selectNodeContents(node);
21256         }
21257         
21258         
21259         range.collapse(true);
21260     
21261         nodeRange.collapse(true);
21262      
21263         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21264         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21265          
21266         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21267         
21268         var nodeIsBefore   =  ss == 1;
21269         var nodeIsAfter    = ee == -1;
21270         
21271         if (nodeIsBefore && nodeIsAfter) {
21272             return 0; // outer
21273         }
21274         if (!nodeIsBefore && nodeIsAfter) {
21275             return 1; //right trailed.
21276         }
21277         
21278         if (nodeIsBefore && !nodeIsAfter) {
21279             return 2;  // left trailed.
21280         }
21281         // fully contined.
21282         return 3;
21283     },
21284
21285     // private? - in a new class?
21286     cleanUpPaste :  function()
21287     {
21288         // cleans up the whole document..
21289         Roo.log('cleanuppaste');
21290         
21291         this.cleanUpChildren(this.doc.body);
21292         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21293         if (clean != this.doc.body.innerHTML) {
21294             this.doc.body.innerHTML = clean;
21295         }
21296         
21297     },
21298     
21299     cleanWordChars : function(input) {// change the chars to hex code
21300         var he = Roo.HtmlEditorCore;
21301         
21302         var output = input;
21303         Roo.each(he.swapCodes, function(sw) { 
21304             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21305             
21306             output = output.replace(swapper, sw[1]);
21307         });
21308         
21309         return output;
21310     },
21311     
21312     
21313     cleanUpChildren : function (n)
21314     {
21315         if (!n.childNodes.length) {
21316             return;
21317         }
21318         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21319            this.cleanUpChild(n.childNodes[i]);
21320         }
21321     },
21322     
21323     
21324         
21325     
21326     cleanUpChild : function (node)
21327     {
21328         var ed = this;
21329         //console.log(node);
21330         if (node.nodeName == "#text") {
21331             // clean up silly Windows -- stuff?
21332             return; 
21333         }
21334         if (node.nodeName == "#comment") {
21335             node.parentNode.removeChild(node);
21336             // clean up silly Windows -- stuff?
21337             return; 
21338         }
21339         var lcname = node.tagName.toLowerCase();
21340         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21341         // whitelist of tags..
21342         
21343         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21344             // remove node.
21345             node.parentNode.removeChild(node);
21346             return;
21347             
21348         }
21349         
21350         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21351         
21352         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21353         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21354         
21355         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21356         //    remove_keep_children = true;
21357         //}
21358         
21359         if (remove_keep_children) {
21360             this.cleanUpChildren(node);
21361             // inserts everything just before this node...
21362             while (node.childNodes.length) {
21363                 var cn = node.childNodes[0];
21364                 node.removeChild(cn);
21365                 node.parentNode.insertBefore(cn, node);
21366             }
21367             node.parentNode.removeChild(node);
21368             return;
21369         }
21370         
21371         if (!node.attributes || !node.attributes.length) {
21372             this.cleanUpChildren(node);
21373             return;
21374         }
21375         
21376         function cleanAttr(n,v)
21377         {
21378             
21379             if (v.match(/^\./) || v.match(/^\//)) {
21380                 return;
21381             }
21382             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21383                 return;
21384             }
21385             if (v.match(/^#/)) {
21386                 return;
21387             }
21388 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21389             node.removeAttribute(n);
21390             
21391         }
21392         
21393         var cwhite = this.cwhite;
21394         var cblack = this.cblack;
21395             
21396         function cleanStyle(n,v)
21397         {
21398             if (v.match(/expression/)) { //XSS?? should we even bother..
21399                 node.removeAttribute(n);
21400                 return;
21401             }
21402             
21403             var parts = v.split(/;/);
21404             var clean = [];
21405             
21406             Roo.each(parts, function(p) {
21407                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21408                 if (!p.length) {
21409                     return true;
21410                 }
21411                 var l = p.split(':').shift().replace(/\s+/g,'');
21412                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21413                 
21414                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21415 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21416                     //node.removeAttribute(n);
21417                     return true;
21418                 }
21419                 //Roo.log()
21420                 // only allow 'c whitelisted system attributes'
21421                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21422 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21423                     //node.removeAttribute(n);
21424                     return true;
21425                 }
21426                 
21427                 
21428                  
21429                 
21430                 clean.push(p);
21431                 return true;
21432             });
21433             if (clean.length) { 
21434                 node.setAttribute(n, clean.join(';'));
21435             } else {
21436                 node.removeAttribute(n);
21437             }
21438             
21439         }
21440         
21441         
21442         for (var i = node.attributes.length-1; i > -1 ; i--) {
21443             var a = node.attributes[i];
21444             //console.log(a);
21445             
21446             if (a.name.toLowerCase().substr(0,2)=='on')  {
21447                 node.removeAttribute(a.name);
21448                 continue;
21449             }
21450             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21451                 node.removeAttribute(a.name);
21452                 continue;
21453             }
21454             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21455                 cleanAttr(a.name,a.value); // fixme..
21456                 continue;
21457             }
21458             if (a.name == 'style') {
21459                 cleanStyle(a.name,a.value);
21460                 continue;
21461             }
21462             /// clean up MS crap..
21463             // tecnically this should be a list of valid class'es..
21464             
21465             
21466             if (a.name == 'class') {
21467                 if (a.value.match(/^Mso/)) {
21468                     node.className = '';
21469                 }
21470                 
21471                 if (a.value.match(/body/)) {
21472                     node.className = '';
21473                 }
21474                 continue;
21475             }
21476             
21477             // style cleanup!?
21478             // class cleanup?
21479             
21480         }
21481         
21482         
21483         this.cleanUpChildren(node);
21484         
21485         
21486     },
21487     
21488     /**
21489      * Clean up MS wordisms...
21490      */
21491     cleanWord : function(node)
21492     {
21493         
21494         
21495         if (!node) {
21496             this.cleanWord(this.doc.body);
21497             return;
21498         }
21499         if (node.nodeName == "#text") {
21500             // clean up silly Windows -- stuff?
21501             return; 
21502         }
21503         if (node.nodeName == "#comment") {
21504             node.parentNode.removeChild(node);
21505             // clean up silly Windows -- stuff?
21506             return; 
21507         }
21508         
21509         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21510             node.parentNode.removeChild(node);
21511             return;
21512         }
21513         
21514         // remove - but keep children..
21515         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21516             while (node.childNodes.length) {
21517                 var cn = node.childNodes[0];
21518                 node.removeChild(cn);
21519                 node.parentNode.insertBefore(cn, node);
21520             }
21521             node.parentNode.removeChild(node);
21522             this.iterateChildren(node, this.cleanWord);
21523             return;
21524         }
21525         // clean styles
21526         if (node.className.length) {
21527             
21528             var cn = node.className.split(/\W+/);
21529             var cna = [];
21530             Roo.each(cn, function(cls) {
21531                 if (cls.match(/Mso[a-zA-Z]+/)) {
21532                     return;
21533                 }
21534                 cna.push(cls);
21535             });
21536             node.className = cna.length ? cna.join(' ') : '';
21537             if (!cna.length) {
21538                 node.removeAttribute("class");
21539             }
21540         }
21541         
21542         if (node.hasAttribute("lang")) {
21543             node.removeAttribute("lang");
21544         }
21545         
21546         if (node.hasAttribute("style")) {
21547             
21548             var styles = node.getAttribute("style").split(";");
21549             var nstyle = [];
21550             Roo.each(styles, function(s) {
21551                 if (!s.match(/:/)) {
21552                     return;
21553                 }
21554                 var kv = s.split(":");
21555                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21556                     return;
21557                 }
21558                 // what ever is left... we allow.
21559                 nstyle.push(s);
21560             });
21561             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21562             if (!nstyle.length) {
21563                 node.removeAttribute('style');
21564             }
21565         }
21566         this.iterateChildren(node, this.cleanWord);
21567         
21568         
21569         
21570     },
21571     /**
21572      * iterateChildren of a Node, calling fn each time, using this as the scole..
21573      * @param {DomNode} node node to iterate children of.
21574      * @param {Function} fn method of this class to call on each item.
21575      */
21576     iterateChildren : function(node, fn)
21577     {
21578         if (!node.childNodes.length) {
21579                 return;
21580         }
21581         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21582            fn.call(this, node.childNodes[i])
21583         }
21584     },
21585     
21586     
21587     /**
21588      * cleanTableWidths.
21589      *
21590      * Quite often pasting from word etc.. results in tables with column and widths.
21591      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21592      *
21593      */
21594     cleanTableWidths : function(node)
21595     {
21596          
21597          
21598         if (!node) {
21599             this.cleanTableWidths(this.doc.body);
21600             return;
21601         }
21602         
21603         // ignore list...
21604         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21605             return; 
21606         }
21607         Roo.log(node.tagName);
21608         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21609             this.iterateChildren(node, this.cleanTableWidths);
21610             return;
21611         }
21612         if (node.hasAttribute('width')) {
21613             node.removeAttribute('width');
21614         }
21615         
21616          
21617         if (node.hasAttribute("style")) {
21618             // pretty basic...
21619             
21620             var styles = node.getAttribute("style").split(";");
21621             var nstyle = [];
21622             Roo.each(styles, function(s) {
21623                 if (!s.match(/:/)) {
21624                     return;
21625                 }
21626                 var kv = s.split(":");
21627                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21628                     return;
21629                 }
21630                 // what ever is left... we allow.
21631                 nstyle.push(s);
21632             });
21633             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21634             if (!nstyle.length) {
21635                 node.removeAttribute('style');
21636             }
21637         }
21638         
21639         this.iterateChildren(node, this.cleanTableWidths);
21640         
21641         
21642     },
21643     
21644     
21645     
21646     
21647     domToHTML : function(currentElement, depth, nopadtext) {
21648         
21649         depth = depth || 0;
21650         nopadtext = nopadtext || false;
21651     
21652         if (!currentElement) {
21653             return this.domToHTML(this.doc.body);
21654         }
21655         
21656         //Roo.log(currentElement);
21657         var j;
21658         var allText = false;
21659         var nodeName = currentElement.nodeName;
21660         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21661         
21662         if  (nodeName == '#text') {
21663             
21664             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21665         }
21666         
21667         
21668         var ret = '';
21669         if (nodeName != 'BODY') {
21670              
21671             var i = 0;
21672             // Prints the node tagName, such as <A>, <IMG>, etc
21673             if (tagName) {
21674                 var attr = [];
21675                 for(i = 0; i < currentElement.attributes.length;i++) {
21676                     // quoting?
21677                     var aname = currentElement.attributes.item(i).name;
21678                     if (!currentElement.attributes.item(i).value.length) {
21679                         continue;
21680                     }
21681                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21682                 }
21683                 
21684                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21685             } 
21686             else {
21687                 
21688                 // eack
21689             }
21690         } else {
21691             tagName = false;
21692         }
21693         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21694             return ret;
21695         }
21696         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21697             nopadtext = true;
21698         }
21699         
21700         
21701         // Traverse the tree
21702         i = 0;
21703         var currentElementChild = currentElement.childNodes.item(i);
21704         var allText = true;
21705         var innerHTML  = '';
21706         lastnode = '';
21707         while (currentElementChild) {
21708             // Formatting code (indent the tree so it looks nice on the screen)
21709             var nopad = nopadtext;
21710             if (lastnode == 'SPAN') {
21711                 nopad  = true;
21712             }
21713             // text
21714             if  (currentElementChild.nodeName == '#text') {
21715                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21716                 toadd = nopadtext ? toadd : toadd.trim();
21717                 if (!nopad && toadd.length > 80) {
21718                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21719                 }
21720                 innerHTML  += toadd;
21721                 
21722                 i++;
21723                 currentElementChild = currentElement.childNodes.item(i);
21724                 lastNode = '';
21725                 continue;
21726             }
21727             allText = false;
21728             
21729             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21730                 
21731             // Recursively traverse the tree structure of the child node
21732             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21733             lastnode = currentElementChild.nodeName;
21734             i++;
21735             currentElementChild=currentElement.childNodes.item(i);
21736         }
21737         
21738         ret += innerHTML;
21739         
21740         if (!allText) {
21741                 // The remaining code is mostly for formatting the tree
21742             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21743         }
21744         
21745         
21746         if (tagName) {
21747             ret+= "</"+tagName+">";
21748         }
21749         return ret;
21750         
21751     },
21752         
21753     applyBlacklists : function()
21754     {
21755         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21756         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21757         
21758         this.white = [];
21759         this.black = [];
21760         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21761             if (b.indexOf(tag) > -1) {
21762                 return;
21763             }
21764             this.white.push(tag);
21765             
21766         }, this);
21767         
21768         Roo.each(w, function(tag) {
21769             if (b.indexOf(tag) > -1) {
21770                 return;
21771             }
21772             if (this.white.indexOf(tag) > -1) {
21773                 return;
21774             }
21775             this.white.push(tag);
21776             
21777         }, this);
21778         
21779         
21780         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21781             if (w.indexOf(tag) > -1) {
21782                 return;
21783             }
21784             this.black.push(tag);
21785             
21786         }, this);
21787         
21788         Roo.each(b, function(tag) {
21789             if (w.indexOf(tag) > -1) {
21790                 return;
21791             }
21792             if (this.black.indexOf(tag) > -1) {
21793                 return;
21794             }
21795             this.black.push(tag);
21796             
21797         }, this);
21798         
21799         
21800         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21801         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21802         
21803         this.cwhite = [];
21804         this.cblack = [];
21805         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21806             if (b.indexOf(tag) > -1) {
21807                 return;
21808             }
21809             this.cwhite.push(tag);
21810             
21811         }, this);
21812         
21813         Roo.each(w, function(tag) {
21814             if (b.indexOf(tag) > -1) {
21815                 return;
21816             }
21817             if (this.cwhite.indexOf(tag) > -1) {
21818                 return;
21819             }
21820             this.cwhite.push(tag);
21821             
21822         }, this);
21823         
21824         
21825         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21826             if (w.indexOf(tag) > -1) {
21827                 return;
21828             }
21829             this.cblack.push(tag);
21830             
21831         }, this);
21832         
21833         Roo.each(b, function(tag) {
21834             if (w.indexOf(tag) > -1) {
21835                 return;
21836             }
21837             if (this.cblack.indexOf(tag) > -1) {
21838                 return;
21839             }
21840             this.cblack.push(tag);
21841             
21842         }, this);
21843     },
21844     
21845     setStylesheets : function(stylesheets)
21846     {
21847         if(typeof(stylesheets) == 'string'){
21848             Roo.get(this.iframe.contentDocument.head).createChild({
21849                 tag : 'link',
21850                 rel : 'stylesheet',
21851                 type : 'text/css',
21852                 href : stylesheets
21853             });
21854             
21855             return;
21856         }
21857         var _this = this;
21858      
21859         Roo.each(stylesheets, function(s) {
21860             if(!s.length){
21861                 return;
21862             }
21863             
21864             Roo.get(_this.iframe.contentDocument.head).createChild({
21865                 tag : 'link',
21866                 rel : 'stylesheet',
21867                 type : 'text/css',
21868                 href : s
21869             });
21870         });
21871
21872         
21873     },
21874     
21875     removeStylesheets : function()
21876     {
21877         var _this = this;
21878         
21879         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21880             s.remove();
21881         });
21882     }
21883     
21884     // hide stuff that is not compatible
21885     /**
21886      * @event blur
21887      * @hide
21888      */
21889     /**
21890      * @event change
21891      * @hide
21892      */
21893     /**
21894      * @event focus
21895      * @hide
21896      */
21897     /**
21898      * @event specialkey
21899      * @hide
21900      */
21901     /**
21902      * @cfg {String} fieldClass @hide
21903      */
21904     /**
21905      * @cfg {String} focusClass @hide
21906      */
21907     /**
21908      * @cfg {String} autoCreate @hide
21909      */
21910     /**
21911      * @cfg {String} inputType @hide
21912      */
21913     /**
21914      * @cfg {String} invalidClass @hide
21915      */
21916     /**
21917      * @cfg {String} invalidText @hide
21918      */
21919     /**
21920      * @cfg {String} msgFx @hide
21921      */
21922     /**
21923      * @cfg {String} validateOnBlur @hide
21924      */
21925 });
21926
21927 Roo.HtmlEditorCore.white = [
21928         'area', 'br', 'img', 'input', 'hr', 'wbr',
21929         
21930        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21931        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21932        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21933        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21934        'table',   'ul',         'xmp', 
21935        
21936        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21937       'thead',   'tr', 
21938      
21939       'dir', 'menu', 'ol', 'ul', 'dl',
21940        
21941       'embed',  'object'
21942 ];
21943
21944
21945 Roo.HtmlEditorCore.black = [
21946     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21947         'applet', // 
21948         'base',   'basefont', 'bgsound', 'blink',  'body', 
21949         'frame',  'frameset', 'head',    'html',   'ilayer', 
21950         'iframe', 'layer',  'link',     'meta',    'object',   
21951         'script', 'style' ,'title',  'xml' // clean later..
21952 ];
21953 Roo.HtmlEditorCore.clean = [
21954     'script', 'style', 'title', 'xml'
21955 ];
21956 Roo.HtmlEditorCore.remove = [
21957     'font'
21958 ];
21959 // attributes..
21960
21961 Roo.HtmlEditorCore.ablack = [
21962     'on'
21963 ];
21964     
21965 Roo.HtmlEditorCore.aclean = [ 
21966     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
21967 ];
21968
21969 // protocols..
21970 Roo.HtmlEditorCore.pwhite= [
21971         'http',  'https',  'mailto'
21972 ];
21973
21974 // white listed style attributes.
21975 Roo.HtmlEditorCore.cwhite= [
21976       //  'text-align', /// default is to allow most things..
21977       
21978          
21979 //        'font-size'//??
21980 ];
21981
21982 // black listed style attributes.
21983 Roo.HtmlEditorCore.cblack= [
21984       //  'font-size' -- this can be set by the project 
21985 ];
21986
21987
21988 Roo.HtmlEditorCore.swapCodes   =[ 
21989     [    8211, "--" ], 
21990     [    8212, "--" ], 
21991     [    8216,  "'" ],  
21992     [    8217, "'" ],  
21993     [    8220, '"' ],  
21994     [    8221, '"' ],  
21995     [    8226, "*" ],  
21996     [    8230, "..." ]
21997 ]; 
21998
21999     /*
22000  * - LGPL
22001  *
22002  * HtmlEditor
22003  * 
22004  */
22005
22006 /**
22007  * @class Roo.bootstrap.HtmlEditor
22008  * @extends Roo.bootstrap.TextArea
22009  * Bootstrap HtmlEditor class
22010
22011  * @constructor
22012  * Create a new HtmlEditor
22013  * @param {Object} config The config object
22014  */
22015
22016 Roo.bootstrap.HtmlEditor = function(config){
22017     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22018     if (!this.toolbars) {
22019         this.toolbars = [];
22020     }
22021     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22022     this.addEvents({
22023             /**
22024              * @event initialize
22025              * Fires when the editor is fully initialized (including the iframe)
22026              * @param {HtmlEditor} this
22027              */
22028             initialize: true,
22029             /**
22030              * @event activate
22031              * Fires when the editor is first receives the focus. Any insertion must wait
22032              * until after this event.
22033              * @param {HtmlEditor} this
22034              */
22035             activate: true,
22036              /**
22037              * @event beforesync
22038              * Fires before the textarea is updated with content from the editor iframe. Return false
22039              * to cancel the sync.
22040              * @param {HtmlEditor} this
22041              * @param {String} html
22042              */
22043             beforesync: true,
22044              /**
22045              * @event beforepush
22046              * Fires before the iframe editor is updated with content from the textarea. Return false
22047              * to cancel the push.
22048              * @param {HtmlEditor} this
22049              * @param {String} html
22050              */
22051             beforepush: true,
22052              /**
22053              * @event sync
22054              * Fires when the textarea is updated with content from the editor iframe.
22055              * @param {HtmlEditor} this
22056              * @param {String} html
22057              */
22058             sync: true,
22059              /**
22060              * @event push
22061              * Fires when the iframe editor is updated with content from the textarea.
22062              * @param {HtmlEditor} this
22063              * @param {String} html
22064              */
22065             push: true,
22066              /**
22067              * @event editmodechange
22068              * Fires when the editor switches edit modes
22069              * @param {HtmlEditor} this
22070              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22071              */
22072             editmodechange: true,
22073             /**
22074              * @event editorevent
22075              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22076              * @param {HtmlEditor} this
22077              */
22078             editorevent: true,
22079             /**
22080              * @event firstfocus
22081              * Fires when on first focus - needed by toolbars..
22082              * @param {HtmlEditor} this
22083              */
22084             firstfocus: true,
22085             /**
22086              * @event autosave
22087              * Auto save the htmlEditor value as a file into Events
22088              * @param {HtmlEditor} this
22089              */
22090             autosave: true,
22091             /**
22092              * @event savedpreview
22093              * preview the saved version of htmlEditor
22094              * @param {HtmlEditor} this
22095              */
22096             savedpreview: true
22097         });
22098 };
22099
22100
22101 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22102     
22103     
22104       /**
22105      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22106      */
22107     toolbars : false,
22108    
22109      /**
22110      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22111      *                        Roo.resizable.
22112      */
22113     resizable : false,
22114      /**
22115      * @cfg {Number} height (in pixels)
22116      */   
22117     height: 300,
22118    /**
22119      * @cfg {Number} width (in pixels)
22120      */   
22121     width: false,
22122     
22123     /**
22124      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22125      * 
22126      */
22127     stylesheets: false,
22128     
22129     // id of frame..
22130     frameId: false,
22131     
22132     // private properties
22133     validationEvent : false,
22134     deferHeight: true,
22135     initialized : false,
22136     activated : false,
22137     
22138     onFocus : Roo.emptyFn,
22139     iframePad:3,
22140     hideMode:'offsets',
22141     
22142     
22143     tbContainer : false,
22144     
22145     toolbarContainer :function() {
22146         return this.wrap.select('.x-html-editor-tb',true).first();
22147     },
22148
22149     /**
22150      * Protected method that will not generally be called directly. It
22151      * is called when the editor creates its toolbar. Override this method if you need to
22152      * add custom toolbar buttons.
22153      * @param {HtmlEditor} editor
22154      */
22155     createToolbar : function(){
22156         
22157         Roo.log("create toolbars");
22158         
22159         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22160         this.toolbars[0].render(this.toolbarContainer());
22161         
22162         return;
22163         
22164 //        if (!editor.toolbars || !editor.toolbars.length) {
22165 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22166 //        }
22167 //        
22168 //        for (var i =0 ; i < editor.toolbars.length;i++) {
22169 //            editor.toolbars[i] = Roo.factory(
22170 //                    typeof(editor.toolbars[i]) == 'string' ?
22171 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
22172 //                Roo.bootstrap.HtmlEditor);
22173 //            editor.toolbars[i].init(editor);
22174 //        }
22175     },
22176
22177      
22178     // private
22179     onRender : function(ct, position)
22180     {
22181        // Roo.log("Call onRender: " + this.xtype);
22182         var _t = this;
22183         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22184       
22185         this.wrap = this.inputEl().wrap({
22186             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22187         });
22188         
22189         this.editorcore.onRender(ct, position);
22190          
22191         if (this.resizable) {
22192             this.resizeEl = new Roo.Resizable(this.wrap, {
22193                 pinned : true,
22194                 wrap: true,
22195                 dynamic : true,
22196                 minHeight : this.height,
22197                 height: this.height,
22198                 handles : this.resizable,
22199                 width: this.width,
22200                 listeners : {
22201                     resize : function(r, w, h) {
22202                         _t.onResize(w,h); // -something
22203                     }
22204                 }
22205             });
22206             
22207         }
22208         this.createToolbar(this);
22209        
22210         
22211         if(!this.width && this.resizable){
22212             this.setSize(this.wrap.getSize());
22213         }
22214         if (this.resizeEl) {
22215             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22216             // should trigger onReize..
22217         }
22218         
22219     },
22220
22221     // private
22222     onResize : function(w, h)
22223     {
22224         Roo.log('resize: ' +w + ',' + h );
22225         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22226         var ew = false;
22227         var eh = false;
22228         
22229         if(this.inputEl() ){
22230             if(typeof w == 'number'){
22231                 var aw = w - this.wrap.getFrameWidth('lr');
22232                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22233                 ew = aw;
22234             }
22235             if(typeof h == 'number'){
22236                  var tbh = -11;  // fixme it needs to tool bar size!
22237                 for (var i =0; i < this.toolbars.length;i++) {
22238                     // fixme - ask toolbars for heights?
22239                     tbh += this.toolbars[i].el.getHeight();
22240                     //if (this.toolbars[i].footer) {
22241                     //    tbh += this.toolbars[i].footer.el.getHeight();
22242                     //}
22243                 }
22244               
22245                 
22246                 
22247                 
22248                 
22249                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22250                 ah -= 5; // knock a few pixes off for look..
22251                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
22252                 var eh = ah;
22253             }
22254         }
22255         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22256         this.editorcore.onResize(ew,eh);
22257         
22258     },
22259
22260     /**
22261      * Toggles the editor between standard and source edit mode.
22262      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22263      */
22264     toggleSourceEdit : function(sourceEditMode)
22265     {
22266         this.editorcore.toggleSourceEdit(sourceEditMode);
22267         
22268         if(this.editorcore.sourceEditMode){
22269             Roo.log('editor - showing textarea');
22270             
22271 //            Roo.log('in');
22272 //            Roo.log(this.syncValue());
22273             this.syncValue();
22274             this.inputEl().removeClass(['hide', 'x-hidden']);
22275             this.inputEl().dom.removeAttribute('tabIndex');
22276             this.inputEl().focus();
22277         }else{
22278             Roo.log('editor - hiding textarea');
22279 //            Roo.log('out')
22280 //            Roo.log(this.pushValue()); 
22281             this.pushValue();
22282             
22283             this.inputEl().addClass(['hide', 'x-hidden']);
22284             this.inputEl().dom.setAttribute('tabIndex', -1);
22285             //this.deferFocus();
22286         }
22287          
22288         if(this.resizable){
22289             this.setSize(this.wrap.getSize());
22290         }
22291         
22292         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22293     },
22294  
22295     // private (for BoxComponent)
22296     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22297
22298     // private (for BoxComponent)
22299     getResizeEl : function(){
22300         return this.wrap;
22301     },
22302
22303     // private (for BoxComponent)
22304     getPositionEl : function(){
22305         return this.wrap;
22306     },
22307
22308     // private
22309     initEvents : function(){
22310         this.originalValue = this.getValue();
22311     },
22312
22313 //    /**
22314 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22315 //     * @method
22316 //     */
22317 //    markInvalid : Roo.emptyFn,
22318 //    /**
22319 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22320 //     * @method
22321 //     */
22322 //    clearInvalid : Roo.emptyFn,
22323
22324     setValue : function(v){
22325         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
22326         this.editorcore.pushValue();
22327     },
22328
22329      
22330     // private
22331     deferFocus : function(){
22332         this.focus.defer(10, this);
22333     },
22334
22335     // doc'ed in Field
22336     focus : function(){
22337         this.editorcore.focus();
22338         
22339     },
22340       
22341
22342     // private
22343     onDestroy : function(){
22344         
22345         
22346         
22347         if(this.rendered){
22348             
22349             for (var i =0; i < this.toolbars.length;i++) {
22350                 // fixme - ask toolbars for heights?
22351                 this.toolbars[i].onDestroy();
22352             }
22353             
22354             this.wrap.dom.innerHTML = '';
22355             this.wrap.remove();
22356         }
22357     },
22358
22359     // private
22360     onFirstFocus : function(){
22361         //Roo.log("onFirstFocus");
22362         this.editorcore.onFirstFocus();
22363          for (var i =0; i < this.toolbars.length;i++) {
22364             this.toolbars[i].onFirstFocus();
22365         }
22366         
22367     },
22368     
22369     // private
22370     syncValue : function()
22371     {   
22372         this.editorcore.syncValue();
22373     },
22374     
22375     pushValue : function()
22376     {   
22377         this.editorcore.pushValue();
22378     }
22379      
22380     
22381     // hide stuff that is not compatible
22382     /**
22383      * @event blur
22384      * @hide
22385      */
22386     /**
22387      * @event change
22388      * @hide
22389      */
22390     /**
22391      * @event focus
22392      * @hide
22393      */
22394     /**
22395      * @event specialkey
22396      * @hide
22397      */
22398     /**
22399      * @cfg {String} fieldClass @hide
22400      */
22401     /**
22402      * @cfg {String} focusClass @hide
22403      */
22404     /**
22405      * @cfg {String} autoCreate @hide
22406      */
22407     /**
22408      * @cfg {String} inputType @hide
22409      */
22410     /**
22411      * @cfg {String} invalidClass @hide
22412      */
22413     /**
22414      * @cfg {String} invalidText @hide
22415      */
22416     /**
22417      * @cfg {String} msgFx @hide
22418      */
22419     /**
22420      * @cfg {String} validateOnBlur @hide
22421      */
22422 });
22423  
22424     
22425    
22426    
22427    
22428       
22429 Roo.namespace('Roo.bootstrap.htmleditor');
22430 /**
22431  * @class Roo.bootstrap.HtmlEditorToolbar1
22432  * Basic Toolbar
22433  * 
22434  * Usage:
22435  *
22436  new Roo.bootstrap.HtmlEditor({
22437     ....
22438     toolbars : [
22439         new Roo.bootstrap.HtmlEditorToolbar1({
22440             disable : { fonts: 1 , format: 1, ..., ... , ...],
22441             btns : [ .... ]
22442         })
22443     }
22444      
22445  * 
22446  * @cfg {Object} disable List of elements to disable..
22447  * @cfg {Array} btns List of additional buttons.
22448  * 
22449  * 
22450  * NEEDS Extra CSS? 
22451  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22452  */
22453  
22454 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22455 {
22456     
22457     Roo.apply(this, config);
22458     
22459     // default disabled, based on 'good practice'..
22460     this.disable = this.disable || {};
22461     Roo.applyIf(this.disable, {
22462         fontSize : true,
22463         colors : true,
22464         specialElements : true
22465     });
22466     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22467     
22468     this.editor = config.editor;
22469     this.editorcore = config.editor.editorcore;
22470     
22471     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22472     
22473     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22474     // dont call parent... till later.
22475 }
22476 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
22477      
22478     bar : true,
22479     
22480     editor : false,
22481     editorcore : false,
22482     
22483     
22484     formats : [
22485         "p" ,  
22486         "h1","h2","h3","h4","h5","h6", 
22487         "pre", "code", 
22488         "abbr", "acronym", "address", "cite", "samp", "var",
22489         'div','span'
22490     ],
22491     
22492     onRender : function(ct, position)
22493     {
22494        // Roo.log("Call onRender: " + this.xtype);
22495         
22496        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22497        Roo.log(this.el);
22498        this.el.dom.style.marginBottom = '0';
22499        var _this = this;
22500        var editorcore = this.editorcore;
22501        var editor= this.editor;
22502        
22503        var children = [];
22504        var btn = function(id,cmd , toggle, handler){
22505        
22506             var  event = toggle ? 'toggle' : 'click';
22507        
22508             var a = {
22509                 size : 'sm',
22510                 xtype: 'Button',
22511                 xns: Roo.bootstrap,
22512                 glyphicon : id,
22513                 cmd : id || cmd,
22514                 enableToggle:toggle !== false,
22515                 //html : 'submit'
22516                 pressed : toggle ? false : null,
22517                 listeners : {}
22518             };
22519             a.listeners[toggle ? 'toggle' : 'click'] = function() {
22520                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
22521             };
22522             children.push(a);
22523             return a;
22524        }
22525         
22526         var style = {
22527                 xtype: 'Button',
22528                 size : 'sm',
22529                 xns: Roo.bootstrap,
22530                 glyphicon : 'font',
22531                 //html : 'submit'
22532                 menu : {
22533                     xtype: 'Menu',
22534                     xns: Roo.bootstrap,
22535                     items:  []
22536                 }
22537         };
22538         Roo.each(this.formats, function(f) {
22539             style.menu.items.push({
22540                 xtype :'MenuItem',
22541                 xns: Roo.bootstrap,
22542                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22543                 tagname : f,
22544                 listeners : {
22545                     click : function()
22546                     {
22547                         editorcore.insertTag(this.tagname);
22548                         editor.focus();
22549                     }
22550                 }
22551                 
22552             });
22553         });
22554          children.push(style);   
22555             
22556             
22557         btn('bold',false,true);
22558         btn('italic',false,true);
22559         btn('align-left', 'justifyleft',true);
22560         btn('align-center', 'justifycenter',true);
22561         btn('align-right' , 'justifyright',true);
22562         btn('link', false, false, function(btn) {
22563             //Roo.log("create link?");
22564             var url = prompt(this.createLinkText, this.defaultLinkValue);
22565             if(url && url != 'http:/'+'/'){
22566                 this.editorcore.relayCmd('createlink', url);
22567             }
22568         }),
22569         btn('list','insertunorderedlist',true);
22570         btn('pencil', false,true, function(btn){
22571                 Roo.log(this);
22572                 
22573                 this.toggleSourceEdit(btn.pressed);
22574         });
22575         /*
22576         var cog = {
22577                 xtype: 'Button',
22578                 size : 'sm',
22579                 xns: Roo.bootstrap,
22580                 glyphicon : 'cog',
22581                 //html : 'submit'
22582                 menu : {
22583                     xtype: 'Menu',
22584                     xns: Roo.bootstrap,
22585                     items:  []
22586                 }
22587         };
22588         
22589         cog.menu.items.push({
22590             xtype :'MenuItem',
22591             xns: Roo.bootstrap,
22592             html : Clean styles,
22593             tagname : f,
22594             listeners : {
22595                 click : function()
22596                 {
22597                     editorcore.insertTag(this.tagname);
22598                     editor.focus();
22599                 }
22600             }
22601             
22602         });
22603        */
22604         
22605          
22606        this.xtype = 'NavSimplebar';
22607         
22608         for(var i=0;i< children.length;i++) {
22609             
22610             this.buttons.add(this.addxtypeChild(children[i]));
22611             
22612         }
22613         
22614         editor.on('editorevent', this.updateToolbar, this);
22615     },
22616     onBtnClick : function(id)
22617     {
22618        this.editorcore.relayCmd(id);
22619        this.editorcore.focus();
22620     },
22621     
22622     /**
22623      * Protected method that will not generally be called directly. It triggers
22624      * a toolbar update by reading the markup state of the current selection in the editor.
22625      */
22626     updateToolbar: function(){
22627
22628         if(!this.editorcore.activated){
22629             this.editor.onFirstFocus(); // is this neeed?
22630             return;
22631         }
22632
22633         var btns = this.buttons; 
22634         var doc = this.editorcore.doc;
22635         btns.get('bold').setActive(doc.queryCommandState('bold'));
22636         btns.get('italic').setActive(doc.queryCommandState('italic'));
22637         //btns.get('underline').setActive(doc.queryCommandState('underline'));
22638         
22639         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22640         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22641         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22642         
22643         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22644         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22645          /*
22646         
22647         var ans = this.editorcore.getAllAncestors();
22648         if (this.formatCombo) {
22649             
22650             
22651             var store = this.formatCombo.store;
22652             this.formatCombo.setValue("");
22653             for (var i =0; i < ans.length;i++) {
22654                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22655                     // select it..
22656                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22657                     break;
22658                 }
22659             }
22660         }
22661         
22662         
22663         
22664         // hides menus... - so this cant be on a menu...
22665         Roo.bootstrap.MenuMgr.hideAll();
22666         */
22667         Roo.bootstrap.MenuMgr.hideAll();
22668         //this.editorsyncValue();
22669     },
22670     onFirstFocus: function() {
22671         this.buttons.each(function(item){
22672            item.enable();
22673         });
22674     },
22675     toggleSourceEdit : function(sourceEditMode){
22676         
22677           
22678         if(sourceEditMode){
22679             Roo.log("disabling buttons");
22680            this.buttons.each( function(item){
22681                 if(item.cmd != 'pencil'){
22682                     item.disable();
22683                 }
22684             });
22685           
22686         }else{
22687             Roo.log("enabling buttons");
22688             if(this.editorcore.initialized){
22689                 this.buttons.each( function(item){
22690                     item.enable();
22691                 });
22692             }
22693             
22694         }
22695         Roo.log("calling toggole on editor");
22696         // tell the editor that it's been pressed..
22697         this.editor.toggleSourceEdit(sourceEditMode);
22698        
22699     }
22700 });
22701
22702
22703
22704
22705
22706 /**
22707  * @class Roo.bootstrap.Table.AbstractSelectionModel
22708  * @extends Roo.util.Observable
22709  * Abstract base class for grid SelectionModels.  It provides the interface that should be
22710  * implemented by descendant classes.  This class should not be directly instantiated.
22711  * @constructor
22712  */
22713 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22714     this.locked = false;
22715     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22716 };
22717
22718
22719 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
22720     /** @ignore Called by the grid automatically. Do not call directly. */
22721     init : function(grid){
22722         this.grid = grid;
22723         this.initEvents();
22724     },
22725
22726     /**
22727      * Locks the selections.
22728      */
22729     lock : function(){
22730         this.locked = true;
22731     },
22732
22733     /**
22734      * Unlocks the selections.
22735      */
22736     unlock : function(){
22737         this.locked = false;
22738     },
22739
22740     /**
22741      * Returns true if the selections are locked.
22742      * @return {Boolean}
22743      */
22744     isLocked : function(){
22745         return this.locked;
22746     }
22747 });
22748 /**
22749  * @extends Roo.bootstrap.Table.AbstractSelectionModel
22750  * @class Roo.bootstrap.Table.RowSelectionModel
22751  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22752  * It supports multiple selections and keyboard selection/navigation. 
22753  * @constructor
22754  * @param {Object} config
22755  */
22756
22757 Roo.bootstrap.Table.RowSelectionModel = function(config){
22758     Roo.apply(this, config);
22759     this.selections = new Roo.util.MixedCollection(false, function(o){
22760         return o.id;
22761     });
22762
22763     this.last = false;
22764     this.lastActive = false;
22765
22766     this.addEvents({
22767         /**
22768              * @event selectionchange
22769              * Fires when the selection changes
22770              * @param {SelectionModel} this
22771              */
22772             "selectionchange" : true,
22773         /**
22774              * @event afterselectionchange
22775              * Fires after the selection changes (eg. by key press or clicking)
22776              * @param {SelectionModel} this
22777              */
22778             "afterselectionchange" : true,
22779         /**
22780              * @event beforerowselect
22781              * Fires when a row is selected being selected, return false to cancel.
22782              * @param {SelectionModel} this
22783              * @param {Number} rowIndex The selected index
22784              * @param {Boolean} keepExisting False if other selections will be cleared
22785              */
22786             "beforerowselect" : true,
22787         /**
22788              * @event rowselect
22789              * Fires when a row is selected.
22790              * @param {SelectionModel} this
22791              * @param {Number} rowIndex The selected index
22792              * @param {Roo.data.Record} r The record
22793              */
22794             "rowselect" : true,
22795         /**
22796              * @event rowdeselect
22797              * Fires when a row is deselected.
22798              * @param {SelectionModel} this
22799              * @param {Number} rowIndex The selected index
22800              */
22801         "rowdeselect" : true
22802     });
22803     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22804     this.locked = false;
22805  };
22806
22807 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
22808     /**
22809      * @cfg {Boolean} singleSelect
22810      * True to allow selection of only one row at a time (defaults to false)
22811      */
22812     singleSelect : false,
22813
22814     // private
22815     initEvents : function()
22816     {
22817
22818         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22819         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
22820         //}else{ // allow click to work like normal
22821          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
22822         //}
22823         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
22824         this.grid.on("rowclick", this.handleMouseDown, this);
22825         
22826         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22827             "up" : function(e){
22828                 if(!e.shiftKey){
22829                     this.selectPrevious(e.shiftKey);
22830                 }else if(this.last !== false && this.lastActive !== false){
22831                     var last = this.last;
22832                     this.selectRange(this.last,  this.lastActive-1);
22833                     this.grid.getView().focusRow(this.lastActive);
22834                     if(last !== false){
22835                         this.last = last;
22836                     }
22837                 }else{
22838                     this.selectFirstRow();
22839                 }
22840                 this.fireEvent("afterselectionchange", this);
22841             },
22842             "down" : function(e){
22843                 if(!e.shiftKey){
22844                     this.selectNext(e.shiftKey);
22845                 }else if(this.last !== false && this.lastActive !== false){
22846                     var last = this.last;
22847                     this.selectRange(this.last,  this.lastActive+1);
22848                     this.grid.getView().focusRow(this.lastActive);
22849                     if(last !== false){
22850                         this.last = last;
22851                     }
22852                 }else{
22853                     this.selectFirstRow();
22854                 }
22855                 this.fireEvent("afterselectionchange", this);
22856             },
22857             scope: this
22858         });
22859         this.grid.store.on('load', function(){
22860             this.selections.clear();
22861         },this);
22862         /*
22863         var view = this.grid.view;
22864         view.on("refresh", this.onRefresh, this);
22865         view.on("rowupdated", this.onRowUpdated, this);
22866         view.on("rowremoved", this.onRemove, this);
22867         */
22868     },
22869
22870     // private
22871     onRefresh : function()
22872     {
22873         var ds = this.grid.store, i, v = this.grid.view;
22874         var s = this.selections;
22875         s.each(function(r){
22876             if((i = ds.indexOfId(r.id)) != -1){
22877                 v.onRowSelect(i);
22878             }else{
22879                 s.remove(r);
22880             }
22881         });
22882     },
22883
22884     // private
22885     onRemove : function(v, index, r){
22886         this.selections.remove(r);
22887     },
22888
22889     // private
22890     onRowUpdated : function(v, index, r){
22891         if(this.isSelected(r)){
22892             v.onRowSelect(index);
22893         }
22894     },
22895
22896     /**
22897      * Select records.
22898      * @param {Array} records The records to select
22899      * @param {Boolean} keepExisting (optional) True to keep existing selections
22900      */
22901     selectRecords : function(records, keepExisting)
22902     {
22903         if(!keepExisting){
22904             this.clearSelections();
22905         }
22906             var ds = this.grid.store;
22907         for(var i = 0, len = records.length; i < len; i++){
22908             this.selectRow(ds.indexOf(records[i]), true);
22909         }
22910     },
22911
22912     /**
22913      * Gets the number of selected rows.
22914      * @return {Number}
22915      */
22916     getCount : function(){
22917         return this.selections.length;
22918     },
22919
22920     /**
22921      * Selects the first row in the grid.
22922      */
22923     selectFirstRow : function(){
22924         this.selectRow(0);
22925     },
22926
22927     /**
22928      * Select the last row.
22929      * @param {Boolean} keepExisting (optional) True to keep existing selections
22930      */
22931     selectLastRow : function(keepExisting){
22932         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22933         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
22934     },
22935
22936     /**
22937      * Selects the row immediately following the last selected row.
22938      * @param {Boolean} keepExisting (optional) True to keep existing selections
22939      */
22940     selectNext : function(keepExisting)
22941     {
22942             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
22943             this.selectRow(this.last+1, keepExisting);
22944             this.grid.getView().focusRow(this.last);
22945         }
22946     },
22947
22948     /**
22949      * Selects the row that precedes the last selected row.
22950      * @param {Boolean} keepExisting (optional) True to keep existing selections
22951      */
22952     selectPrevious : function(keepExisting){
22953         if(this.last){
22954             this.selectRow(this.last-1, keepExisting);
22955             this.grid.getView().focusRow(this.last);
22956         }
22957     },
22958
22959     /**
22960      * Returns the selected records
22961      * @return {Array} Array of selected records
22962      */
22963     getSelections : function(){
22964         return [].concat(this.selections.items);
22965     },
22966
22967     /**
22968      * Returns the first selected record.
22969      * @return {Record}
22970      */
22971     getSelected : function(){
22972         return this.selections.itemAt(0);
22973     },
22974
22975
22976     /**
22977      * Clears all selections.
22978      */
22979     clearSelections : function(fast)
22980     {
22981         if(this.locked) {
22982             return;
22983         }
22984         if(fast !== true){
22985                 var ds = this.grid.store;
22986             var s = this.selections;
22987             s.each(function(r){
22988                 this.deselectRow(ds.indexOfId(r.id));
22989             }, this);
22990             s.clear();
22991         }else{
22992             this.selections.clear();
22993         }
22994         this.last = false;
22995     },
22996
22997
22998     /**
22999      * Selects all rows.
23000      */
23001     selectAll : function(){
23002         if(this.locked) {
23003             return;
23004         }
23005         this.selections.clear();
23006         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23007             this.selectRow(i, true);
23008         }
23009     },
23010
23011     /**
23012      * Returns True if there is a selection.
23013      * @return {Boolean}
23014      */
23015     hasSelection : function(){
23016         return this.selections.length > 0;
23017     },
23018
23019     /**
23020      * Returns True if the specified row is selected.
23021      * @param {Number/Record} record The record or index of the record to check
23022      * @return {Boolean}
23023      */
23024     isSelected : function(index){
23025             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23026         return (r && this.selections.key(r.id) ? true : false);
23027     },
23028
23029     /**
23030      * Returns True if the specified record id is selected.
23031      * @param {String} id The id of record to check
23032      * @return {Boolean}
23033      */
23034     isIdSelected : function(id){
23035         return (this.selections.key(id) ? true : false);
23036     },
23037
23038
23039     // private
23040     handleMouseDBClick : function(e, t){
23041         
23042     },
23043     // private
23044     handleMouseDown : function(e, t)
23045     {
23046             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23047         if(this.isLocked() || rowIndex < 0 ){
23048             return;
23049         };
23050         if(e.shiftKey && this.last !== false){
23051             var last = this.last;
23052             this.selectRange(last, rowIndex, e.ctrlKey);
23053             this.last = last; // reset the last
23054             t.focus();
23055     
23056         }else{
23057             var isSelected = this.isSelected(rowIndex);
23058             //Roo.log("select row:" + rowIndex);
23059             if(isSelected){
23060                 this.deselectRow(rowIndex);
23061             } else {
23062                         this.selectRow(rowIndex, true);
23063             }
23064     
23065             /*
23066                 if(e.button !== 0 && isSelected){
23067                 alert('rowIndex 2: ' + rowIndex);
23068                     view.focusRow(rowIndex);
23069                 }else if(e.ctrlKey && isSelected){
23070                     this.deselectRow(rowIndex);
23071                 }else if(!isSelected){
23072                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23073                     view.focusRow(rowIndex);
23074                 }
23075             */
23076         }
23077         this.fireEvent("afterselectionchange", this);
23078     },
23079     // private
23080     handleDragableRowClick :  function(grid, rowIndex, e) 
23081     {
23082         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23083             this.selectRow(rowIndex, false);
23084             grid.view.focusRow(rowIndex);
23085              this.fireEvent("afterselectionchange", this);
23086         }
23087     },
23088     
23089     /**
23090      * Selects multiple rows.
23091      * @param {Array} rows Array of the indexes of the row to select
23092      * @param {Boolean} keepExisting (optional) True to keep existing selections
23093      */
23094     selectRows : function(rows, keepExisting){
23095         if(!keepExisting){
23096             this.clearSelections();
23097         }
23098         for(var i = 0, len = rows.length; i < len; i++){
23099             this.selectRow(rows[i], true);
23100         }
23101     },
23102
23103     /**
23104      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23105      * @param {Number} startRow The index of the first row in the range
23106      * @param {Number} endRow The index of the last row in the range
23107      * @param {Boolean} keepExisting (optional) True to retain existing selections
23108      */
23109     selectRange : function(startRow, endRow, keepExisting){
23110         if(this.locked) {
23111             return;
23112         }
23113         if(!keepExisting){
23114             this.clearSelections();
23115         }
23116         if(startRow <= endRow){
23117             for(var i = startRow; i <= endRow; i++){
23118                 this.selectRow(i, true);
23119             }
23120         }else{
23121             for(var i = startRow; i >= endRow; i--){
23122                 this.selectRow(i, true);
23123             }
23124         }
23125     },
23126
23127     /**
23128      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23129      * @param {Number} startRow The index of the first row in the range
23130      * @param {Number} endRow The index of the last row in the range
23131      */
23132     deselectRange : function(startRow, endRow, preventViewNotify){
23133         if(this.locked) {
23134             return;
23135         }
23136         for(var i = startRow; i <= endRow; i++){
23137             this.deselectRow(i, preventViewNotify);
23138         }
23139     },
23140
23141     /**
23142      * Selects a row.
23143      * @param {Number} row The index of the row to select
23144      * @param {Boolean} keepExisting (optional) True to keep existing selections
23145      */
23146     selectRow : function(index, keepExisting, preventViewNotify)
23147     {
23148             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23149             return;
23150         }
23151         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23152             if(!keepExisting || this.singleSelect){
23153                 this.clearSelections();
23154             }
23155             
23156             var r = this.grid.store.getAt(index);
23157             //console.log('selectRow - record id :' + r.id);
23158             
23159             this.selections.add(r);
23160             this.last = this.lastActive = index;
23161             if(!preventViewNotify){
23162                 var proxy = new Roo.Element(
23163                                 this.grid.getRowDom(index)
23164                 );
23165                 proxy.addClass('bg-info info');
23166             }
23167             this.fireEvent("rowselect", this, index, r);
23168             this.fireEvent("selectionchange", this);
23169         }
23170     },
23171
23172     /**
23173      * Deselects a row.
23174      * @param {Number} row The index of the row to deselect
23175      */
23176     deselectRow : function(index, preventViewNotify)
23177     {
23178         if(this.locked) {
23179             return;
23180         }
23181         if(this.last == index){
23182             this.last = false;
23183         }
23184         if(this.lastActive == index){
23185             this.lastActive = false;
23186         }
23187         
23188         var r = this.grid.store.getAt(index);
23189         if (!r) {
23190             return;
23191         }
23192         
23193         this.selections.remove(r);
23194         //.console.log('deselectRow - record id :' + r.id);
23195         if(!preventViewNotify){
23196         
23197             var proxy = new Roo.Element(
23198                 this.grid.getRowDom(index)
23199             );
23200             proxy.removeClass('bg-info info');
23201         }
23202         this.fireEvent("rowdeselect", this, index);
23203         this.fireEvent("selectionchange", this);
23204     },
23205
23206     // private
23207     restoreLast : function(){
23208         if(this._last){
23209             this.last = this._last;
23210         }
23211     },
23212
23213     // private
23214     acceptsNav : function(row, col, cm){
23215         return !cm.isHidden(col) && cm.isCellEditable(col, row);
23216     },
23217
23218     // private
23219     onEditorKey : function(field, e){
23220         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23221         if(k == e.TAB){
23222             e.stopEvent();
23223             ed.completeEdit();
23224             if(e.shiftKey){
23225                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23226             }else{
23227                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23228             }
23229         }else if(k == e.ENTER && !e.ctrlKey){
23230             e.stopEvent();
23231             ed.completeEdit();
23232             if(e.shiftKey){
23233                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
23234             }else{
23235                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
23236             }
23237         }else if(k == e.ESC){
23238             ed.cancelEdit();
23239         }
23240         if(newCell){
23241             g.startEditing(newCell[0], newCell[1]);
23242         }
23243     }
23244 });
23245 /*
23246  * Based on:
23247  * Ext JS Library 1.1.1
23248  * Copyright(c) 2006-2007, Ext JS, LLC.
23249  *
23250  * Originally Released Under LGPL - original licence link has changed is not relivant.
23251  *
23252  * Fork - LGPL
23253  * <script type="text/javascript">
23254  */
23255  
23256 /**
23257  * @class Roo.bootstrap.PagingToolbar
23258  * @extends Roo.bootstrap.NavSimplebar
23259  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
23260  * @constructor
23261  * Create a new PagingToolbar
23262  * @param {Object} config The config object
23263  * @param {Roo.data.Store} store
23264  */
23265 Roo.bootstrap.PagingToolbar = function(config)
23266 {
23267     // old args format still supported... - xtype is prefered..
23268         // created from xtype...
23269     
23270     this.ds = config.dataSource;
23271     
23272     if (config.store && !this.ds) {
23273         this.store= Roo.factory(config.store, Roo.data);
23274         this.ds = this.store;
23275         this.ds.xmodule = this.xmodule || false;
23276     }
23277     
23278     this.toolbarItems = [];
23279     if (config.items) {
23280         this.toolbarItems = config.items;
23281     }
23282     
23283     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
23284     
23285     this.cursor = 0;
23286     
23287     if (this.ds) { 
23288         this.bind(this.ds);
23289     }
23290     
23291     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
23292     
23293 };
23294
23295 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
23296     /**
23297      * @cfg {Roo.data.Store} dataSource
23298      * The underlying data store providing the paged data
23299      */
23300     /**
23301      * @cfg {String/HTMLElement/Element} container
23302      * container The id or element that will contain the toolbar
23303      */
23304     /**
23305      * @cfg {Boolean} displayInfo
23306      * True to display the displayMsg (defaults to false)
23307      */
23308     /**
23309      * @cfg {Number} pageSize
23310      * The number of records to display per page (defaults to 20)
23311      */
23312     pageSize: 20,
23313     /**
23314      * @cfg {String} displayMsg
23315      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
23316      */
23317     displayMsg : 'Displaying {0} - {1} of {2}',
23318     /**
23319      * @cfg {String} emptyMsg
23320      * The message to display when no records are found (defaults to "No data to display")
23321      */
23322     emptyMsg : 'No data to display',
23323     /**
23324      * Customizable piece of the default paging text (defaults to "Page")
23325      * @type String
23326      */
23327     beforePageText : "Page",
23328     /**
23329      * Customizable piece of the default paging text (defaults to "of %0")
23330      * @type String
23331      */
23332     afterPageText : "of {0}",
23333     /**
23334      * Customizable piece of the default paging text (defaults to "First Page")
23335      * @type String
23336      */
23337     firstText : "First Page",
23338     /**
23339      * Customizable piece of the default paging text (defaults to "Previous Page")
23340      * @type String
23341      */
23342     prevText : "Previous Page",
23343     /**
23344      * Customizable piece of the default paging text (defaults to "Next Page")
23345      * @type String
23346      */
23347     nextText : "Next Page",
23348     /**
23349      * Customizable piece of the default paging text (defaults to "Last Page")
23350      * @type String
23351      */
23352     lastText : "Last Page",
23353     /**
23354      * Customizable piece of the default paging text (defaults to "Refresh")
23355      * @type String
23356      */
23357     refreshText : "Refresh",
23358
23359     buttons : false,
23360     // private
23361     onRender : function(ct, position) 
23362     {
23363         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
23364         this.navgroup.parentId = this.id;
23365         this.navgroup.onRender(this.el, null);
23366         // add the buttons to the navgroup
23367         
23368         if(this.displayInfo){
23369             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
23370             this.displayEl = this.el.select('.x-paging-info', true).first();
23371 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
23372 //            this.displayEl = navel.el.select('span',true).first();
23373         }
23374         
23375         var _this = this;
23376         
23377         if(this.buttons){
23378             Roo.each(_this.buttons, function(e){ // this might need to use render????
23379                Roo.factory(e).onRender(_this.el, null);
23380             });
23381         }
23382             
23383         Roo.each(_this.toolbarItems, function(e) {
23384             _this.navgroup.addItem(e);
23385         });
23386         
23387         
23388         this.first = this.navgroup.addItem({
23389             tooltip: this.firstText,
23390             cls: "prev",
23391             icon : 'fa fa-backward',
23392             disabled: true,
23393             preventDefault: true,
23394             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23395         });
23396         
23397         this.prev =  this.navgroup.addItem({
23398             tooltip: this.prevText,
23399             cls: "prev",
23400             icon : 'fa fa-step-backward',
23401             disabled: true,
23402             preventDefault: true,
23403             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
23404         });
23405     //this.addSeparator();
23406         
23407         
23408         var field = this.navgroup.addItem( {
23409             tagtype : 'span',
23410             cls : 'x-paging-position',
23411             
23412             html : this.beforePageText  +
23413                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23414                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
23415          } ); //?? escaped?
23416         
23417         this.field = field.el.select('input', true).first();
23418         this.field.on("keydown", this.onPagingKeydown, this);
23419         this.field.on("focus", function(){this.dom.select();});
23420     
23421     
23422         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
23423         //this.field.setHeight(18);
23424         //this.addSeparator();
23425         this.next = this.navgroup.addItem({
23426             tooltip: this.nextText,
23427             cls: "next",
23428             html : ' <i class="fa fa-step-forward">',
23429             disabled: true,
23430             preventDefault: true,
23431             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
23432         });
23433         this.last = this.navgroup.addItem({
23434             tooltip: this.lastText,
23435             icon : 'fa fa-forward',
23436             cls: "next",
23437             disabled: true,
23438             preventDefault: true,
23439             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
23440         });
23441     //this.addSeparator();
23442         this.loading = this.navgroup.addItem({
23443             tooltip: this.refreshText,
23444             icon: 'fa fa-refresh',
23445             preventDefault: true,
23446             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23447         });
23448         
23449     },
23450
23451     // private
23452     updateInfo : function(){
23453         if(this.displayEl){
23454             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23455             var msg = count == 0 ?
23456                 this.emptyMsg :
23457                 String.format(
23458                     this.displayMsg,
23459                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
23460                 );
23461             this.displayEl.update(msg);
23462         }
23463     },
23464
23465     // private
23466     onLoad : function(ds, r, o){
23467        this.cursor = o.params ? o.params.start : 0;
23468        var d = this.getPageData(),
23469             ap = d.activePage,
23470             ps = d.pages;
23471         
23472        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23473        this.field.dom.value = ap;
23474        this.first.setDisabled(ap == 1);
23475        this.prev.setDisabled(ap == 1);
23476        this.next.setDisabled(ap == ps);
23477        this.last.setDisabled(ap == ps);
23478        this.loading.enable();
23479        this.updateInfo();
23480     },
23481
23482     // private
23483     getPageData : function(){
23484         var total = this.ds.getTotalCount();
23485         return {
23486             total : total,
23487             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23488             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23489         };
23490     },
23491
23492     // private
23493     onLoadError : function(){
23494         this.loading.enable();
23495     },
23496
23497     // private
23498     onPagingKeydown : function(e){
23499         var k = e.getKey();
23500         var d = this.getPageData();
23501         if(k == e.RETURN){
23502             var v = this.field.dom.value, pageNum;
23503             if(!v || isNaN(pageNum = parseInt(v, 10))){
23504                 this.field.dom.value = d.activePage;
23505                 return;
23506             }
23507             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23508             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23509             e.stopEvent();
23510         }
23511         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))
23512         {
23513           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23514           this.field.dom.value = pageNum;
23515           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23516           e.stopEvent();
23517         }
23518         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23519         {
23520           var v = this.field.dom.value, pageNum; 
23521           var increment = (e.shiftKey) ? 10 : 1;
23522           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23523                 increment *= -1;
23524           }
23525           if(!v || isNaN(pageNum = parseInt(v, 10))) {
23526             this.field.dom.value = d.activePage;
23527             return;
23528           }
23529           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23530           {
23531             this.field.dom.value = parseInt(v, 10) + increment;
23532             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23533             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23534           }
23535           e.stopEvent();
23536         }
23537     },
23538
23539     // private
23540     beforeLoad : function(){
23541         if(this.loading){
23542             this.loading.disable();
23543         }
23544     },
23545
23546     // private
23547     onClick : function(which){
23548         
23549         var ds = this.ds;
23550         if (!ds) {
23551             return;
23552         }
23553         
23554         switch(which){
23555             case "first":
23556                 ds.load({params:{start: 0, limit: this.pageSize}});
23557             break;
23558             case "prev":
23559                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23560             break;
23561             case "next":
23562                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23563             break;
23564             case "last":
23565                 var total = ds.getTotalCount();
23566                 var extra = total % this.pageSize;
23567                 var lastStart = extra ? (total - extra) : total-this.pageSize;
23568                 ds.load({params:{start: lastStart, limit: this.pageSize}});
23569             break;
23570             case "refresh":
23571                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23572             break;
23573         }
23574     },
23575
23576     /**
23577      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23578      * @param {Roo.data.Store} store The data store to unbind
23579      */
23580     unbind : function(ds){
23581         ds.un("beforeload", this.beforeLoad, this);
23582         ds.un("load", this.onLoad, this);
23583         ds.un("loadexception", this.onLoadError, this);
23584         ds.un("remove", this.updateInfo, this);
23585         ds.un("add", this.updateInfo, this);
23586         this.ds = undefined;
23587     },
23588
23589     /**
23590      * Binds the paging toolbar to the specified {@link Roo.data.Store}
23591      * @param {Roo.data.Store} store The data store to bind
23592      */
23593     bind : function(ds){
23594         ds.on("beforeload", this.beforeLoad, this);
23595         ds.on("load", this.onLoad, this);
23596         ds.on("loadexception", this.onLoadError, this);
23597         ds.on("remove", this.updateInfo, this);
23598         ds.on("add", this.updateInfo, this);
23599         this.ds = ds;
23600     }
23601 });/*
23602  * - LGPL
23603  *
23604  * element
23605  * 
23606  */
23607
23608 /**
23609  * @class Roo.bootstrap.MessageBar
23610  * @extends Roo.bootstrap.Component
23611  * Bootstrap MessageBar class
23612  * @cfg {String} html contents of the MessageBar
23613  * @cfg {String} weight (info | success | warning | danger) default info
23614  * @cfg {String} beforeClass insert the bar before the given class
23615  * @cfg {Boolean} closable (true | false) default false
23616  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23617  * 
23618  * @constructor
23619  * Create a new Element
23620  * @param {Object} config The config object
23621  */
23622
23623 Roo.bootstrap.MessageBar = function(config){
23624     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23625 };
23626
23627 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
23628     
23629     html: '',
23630     weight: 'info',
23631     closable: false,
23632     fixed: false,
23633     beforeClass: 'bootstrap-sticky-wrap',
23634     
23635     getAutoCreate : function(){
23636         
23637         var cfg = {
23638             tag: 'div',
23639             cls: 'alert alert-dismissable alert-' + this.weight,
23640             cn: [
23641                 {
23642                     tag: 'span',
23643                     cls: 'message',
23644                     html: this.html || ''
23645                 }
23646             ]
23647         };
23648         
23649         if(this.fixed){
23650             cfg.cls += ' alert-messages-fixed';
23651         }
23652         
23653         if(this.closable){
23654             cfg.cn.push({
23655                 tag: 'button',
23656                 cls: 'close',
23657                 html: 'x'
23658             });
23659         }
23660         
23661         return cfg;
23662     },
23663     
23664     onRender : function(ct, position)
23665     {
23666         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23667         
23668         if(!this.el){
23669             var cfg = Roo.apply({},  this.getAutoCreate());
23670             cfg.id = Roo.id();
23671             
23672             if (this.cls) {
23673                 cfg.cls += ' ' + this.cls;
23674             }
23675             if (this.style) {
23676                 cfg.style = this.style;
23677             }
23678             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23679             
23680             this.el.setVisibilityMode(Roo.Element.DISPLAY);
23681         }
23682         
23683         this.el.select('>button.close').on('click', this.hide, this);
23684         
23685     },
23686     
23687     show : function()
23688     {
23689         if (!this.rendered) {
23690             this.render();
23691         }
23692         
23693         this.el.show();
23694         
23695         this.fireEvent('show', this);
23696         
23697     },
23698     
23699     hide : function()
23700     {
23701         if (!this.rendered) {
23702             this.render();
23703         }
23704         
23705         this.el.hide();
23706         
23707         this.fireEvent('hide', this);
23708     },
23709     
23710     update : function()
23711     {
23712 //        var e = this.el.dom.firstChild;
23713 //        
23714 //        if(this.closable){
23715 //            e = e.nextSibling;
23716 //        }
23717 //        
23718 //        e.data = this.html || '';
23719
23720         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23721     }
23722    
23723 });
23724
23725  
23726
23727      /*
23728  * - LGPL
23729  *
23730  * Graph
23731  * 
23732  */
23733
23734
23735 /**
23736  * @class Roo.bootstrap.Graph
23737  * @extends Roo.bootstrap.Component
23738  * Bootstrap Graph class
23739 > Prameters
23740  -sm {number} sm 4
23741  -md {number} md 5
23742  @cfg {String} graphtype  bar | vbar | pie
23743  @cfg {number} g_x coodinator | centre x (pie)
23744  @cfg {number} g_y coodinator | centre y (pie)
23745  @cfg {number} g_r radius (pie)
23746  @cfg {number} g_height height of the chart (respected by all elements in the set)
23747  @cfg {number} g_width width of the chart (respected by all elements in the set)
23748  @cfg {Object} title The title of the chart
23749     
23750  -{Array}  values
23751  -opts (object) options for the chart 
23752      o {
23753      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23754      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23755      o vgutter (number)
23756      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.
23757      o stacked (boolean) whether or not to tread values as in a stacked bar chart
23758      o to
23759      o stretch (boolean)
23760      o }
23761  -opts (object) options for the pie
23762      o{
23763      o cut
23764      o startAngle (number)
23765      o endAngle (number)
23766      } 
23767  *
23768  * @constructor
23769  * Create a new Input
23770  * @param {Object} config The config object
23771  */
23772
23773 Roo.bootstrap.Graph = function(config){
23774     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23775     
23776     this.addEvents({
23777         // img events
23778         /**
23779          * @event click
23780          * The img click event for the img.
23781          * @param {Roo.EventObject} e
23782          */
23783         "click" : true
23784     });
23785 };
23786
23787 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
23788     
23789     sm: 4,
23790     md: 5,
23791     graphtype: 'bar',
23792     g_height: 250,
23793     g_width: 400,
23794     g_x: 50,
23795     g_y: 50,
23796     g_r: 30,
23797     opts:{
23798         //g_colors: this.colors,
23799         g_type: 'soft',
23800         g_gutter: '20%'
23801
23802     },
23803     title : false,
23804
23805     getAutoCreate : function(){
23806         
23807         var cfg = {
23808             tag: 'div',
23809             html : null
23810         };
23811         
23812         
23813         return  cfg;
23814     },
23815
23816     onRender : function(ct,position){
23817         
23818         
23819         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23820         
23821         if (typeof(Raphael) == 'undefined') {
23822             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23823             return;
23824         }
23825         
23826         this.raphael = Raphael(this.el.dom);
23827         
23828                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23829                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23830                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23831                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23832                 /*
23833                 r.text(160, 10, "Single Series Chart").attr(txtattr);
23834                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23835                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23836                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23837                 
23838                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23839                 r.barchart(330, 10, 300, 220, data1);
23840                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23841                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23842                 */
23843                 
23844                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23845                 // r.barchart(30, 30, 560, 250,  xdata, {
23846                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23847                 //     axis : "0 0 1 1",
23848                 //     axisxlabels :  xdata
23849                 //     //yvalues : cols,
23850                    
23851                 // });
23852 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23853 //        
23854 //        this.load(null,xdata,{
23855 //                axis : "0 0 1 1",
23856 //                axisxlabels :  xdata
23857 //                });
23858
23859     },
23860
23861     load : function(graphtype,xdata,opts)
23862     {
23863         this.raphael.clear();
23864         if(!graphtype) {
23865             graphtype = this.graphtype;
23866         }
23867         if(!opts){
23868             opts = this.opts;
23869         }
23870         var r = this.raphael,
23871             fin = function () {
23872                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23873             },
23874             fout = function () {
23875                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23876             },
23877             pfin = function() {
23878                 this.sector.stop();
23879                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23880
23881                 if (this.label) {
23882                     this.label[0].stop();
23883                     this.label[0].attr({ r: 7.5 });
23884                     this.label[1].attr({ "font-weight": 800 });
23885                 }
23886             },
23887             pfout = function() {
23888                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23889
23890                 if (this.label) {
23891                     this.label[0].animate({ r: 5 }, 500, "bounce");
23892                     this.label[1].attr({ "font-weight": 400 });
23893                 }
23894             };
23895
23896         switch(graphtype){
23897             case 'bar':
23898                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23899                 break;
23900             case 'hbar':
23901                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23902                 break;
23903             case 'pie':
23904 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
23905 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23906 //            
23907                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23908                 
23909                 break;
23910
23911         }
23912         
23913         if(this.title){
23914             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23915         }
23916         
23917     },
23918     
23919     setTitle: function(o)
23920     {
23921         this.title = o;
23922     },
23923     
23924     initEvents: function() {
23925         
23926         if(!this.href){
23927             this.el.on('click', this.onClick, this);
23928         }
23929     },
23930     
23931     onClick : function(e)
23932     {
23933         Roo.log('img onclick');
23934         this.fireEvent('click', this, e);
23935     }
23936    
23937 });
23938
23939  
23940 /*
23941  * - LGPL
23942  *
23943  * numberBox
23944  * 
23945  */
23946 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23947
23948 /**
23949  * @class Roo.bootstrap.dash.NumberBox
23950  * @extends Roo.bootstrap.Component
23951  * Bootstrap NumberBox class
23952  * @cfg {String} headline Box headline
23953  * @cfg {String} content Box content
23954  * @cfg {String} icon Box icon
23955  * @cfg {String} footer Footer text
23956  * @cfg {String} fhref Footer href
23957  * 
23958  * @constructor
23959  * Create a new NumberBox
23960  * @param {Object} config The config object
23961  */
23962
23963
23964 Roo.bootstrap.dash.NumberBox = function(config){
23965     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23966     
23967 };
23968
23969 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
23970     
23971     headline : '',
23972     content : '',
23973     icon : '',
23974     footer : '',
23975     fhref : '',
23976     ficon : '',
23977     
23978     getAutoCreate : function(){
23979         
23980         var cfg = {
23981             tag : 'div',
23982             cls : 'small-box ',
23983             cn : [
23984                 {
23985                     tag : 'div',
23986                     cls : 'inner',
23987                     cn :[
23988                         {
23989                             tag : 'h3',
23990                             cls : 'roo-headline',
23991                             html : this.headline
23992                         },
23993                         {
23994                             tag : 'p',
23995                             cls : 'roo-content',
23996                             html : this.content
23997                         }
23998                     ]
23999                 }
24000             ]
24001         };
24002         
24003         if(this.icon){
24004             cfg.cn.push({
24005                 tag : 'div',
24006                 cls : 'icon',
24007                 cn :[
24008                     {
24009                         tag : 'i',
24010                         cls : 'ion ' + this.icon
24011                     }
24012                 ]
24013             });
24014         }
24015         
24016         if(this.footer){
24017             var footer = {
24018                 tag : 'a',
24019                 cls : 'small-box-footer',
24020                 href : this.fhref || '#',
24021                 html : this.footer
24022             };
24023             
24024             cfg.cn.push(footer);
24025             
24026         }
24027         
24028         return  cfg;
24029     },
24030
24031     onRender : function(ct,position){
24032         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24033
24034
24035        
24036                 
24037     },
24038
24039     setHeadline: function (value)
24040     {
24041         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24042     },
24043     
24044     setFooter: function (value, href)
24045     {
24046         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24047         
24048         if(href){
24049             this.el.select('a.small-box-footer',true).first().attr('href', href);
24050         }
24051         
24052     },
24053
24054     setContent: function (value)
24055     {
24056         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24057     },
24058
24059     initEvents: function() 
24060     {   
24061         
24062     }
24063     
24064 });
24065
24066  
24067 /*
24068  * - LGPL
24069  *
24070  * TabBox
24071  * 
24072  */
24073 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24074
24075 /**
24076  * @class Roo.bootstrap.dash.TabBox
24077  * @extends Roo.bootstrap.Component
24078  * Bootstrap TabBox class
24079  * @cfg {String} title Title of the TabBox
24080  * @cfg {String} icon Icon of the TabBox
24081  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24082  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24083  * 
24084  * @constructor
24085  * Create a new TabBox
24086  * @param {Object} config The config object
24087  */
24088
24089
24090 Roo.bootstrap.dash.TabBox = function(config){
24091     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24092     this.addEvents({
24093         // raw events
24094         /**
24095          * @event addpane
24096          * When a pane is added
24097          * @param {Roo.bootstrap.dash.TabPane} pane
24098          */
24099         "addpane" : true,
24100         /**
24101          * @event activatepane
24102          * When a pane is activated
24103          * @param {Roo.bootstrap.dash.TabPane} pane
24104          */
24105         "activatepane" : true
24106         
24107          
24108     });
24109     
24110     this.panes = [];
24111 };
24112
24113 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24114
24115     title : '',
24116     icon : false,
24117     showtabs : true,
24118     tabScrollable : false,
24119     
24120     getChildContainer : function()
24121     {
24122         return this.el.select('.tab-content', true).first();
24123     },
24124     
24125     getAutoCreate : function(){
24126         
24127         var header = {
24128             tag: 'li',
24129             cls: 'pull-left header',
24130             html: this.title,
24131             cn : []
24132         };
24133         
24134         if(this.icon){
24135             header.cn.push({
24136                 tag: 'i',
24137                 cls: 'fa ' + this.icon
24138             });
24139         }
24140         
24141         var h = {
24142             tag: 'ul',
24143             cls: 'nav nav-tabs pull-right',
24144             cn: [
24145                 header
24146             ]
24147         };
24148         
24149         if(this.tabScrollable){
24150             h = {
24151                 tag: 'div',
24152                 cls: 'tab-header',
24153                 cn: [
24154                     {
24155                         tag: 'ul',
24156                         cls: 'nav nav-tabs pull-right',
24157                         cn: [
24158                             header
24159                         ]
24160                     }
24161                 ]
24162             };
24163         }
24164         
24165         var cfg = {
24166             tag: 'div',
24167             cls: 'nav-tabs-custom',
24168             cn: [
24169                 h,
24170                 {
24171                     tag: 'div',
24172                     cls: 'tab-content no-padding',
24173                     cn: []
24174                 }
24175             ]
24176         };
24177
24178         return  cfg;
24179     },
24180     initEvents : function()
24181     {
24182         //Roo.log('add add pane handler');
24183         this.on('addpane', this.onAddPane, this);
24184     },
24185      /**
24186      * Updates the box title
24187      * @param {String} html to set the title to.
24188      */
24189     setTitle : function(value)
24190     {
24191         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24192     },
24193     onAddPane : function(pane)
24194     {
24195         this.panes.push(pane);
24196         //Roo.log('addpane');
24197         //Roo.log(pane);
24198         // tabs are rendere left to right..
24199         if(!this.showtabs){
24200             return;
24201         }
24202         
24203         var ctr = this.el.select('.nav-tabs', true).first();
24204          
24205          
24206         var existing = ctr.select('.nav-tab',true);
24207         var qty = existing.getCount();;
24208         
24209         
24210         var tab = ctr.createChild({
24211             tag : 'li',
24212             cls : 'nav-tab' + (qty ? '' : ' active'),
24213             cn : [
24214                 {
24215                     tag : 'a',
24216                     href:'#',
24217                     html : pane.title
24218                 }
24219             ]
24220         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24221         pane.tab = tab;
24222         
24223         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24224         if (!qty) {
24225             pane.el.addClass('active');
24226         }
24227         
24228                 
24229     },
24230     onTabClick : function(ev,un,ob,pane)
24231     {
24232         //Roo.log('tab - prev default');
24233         ev.preventDefault();
24234         
24235         
24236         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
24237         pane.tab.addClass('active');
24238         //Roo.log(pane.title);
24239         this.getChildContainer().select('.tab-pane',true).removeClass('active');
24240         // technically we should have a deactivate event.. but maybe add later.
24241         // and it should not de-activate the selected tab...
24242         this.fireEvent('activatepane', pane);
24243         pane.el.addClass('active');
24244         pane.fireEvent('activate');
24245         
24246         
24247     },
24248     
24249     getActivePane : function()
24250     {
24251         var r = false;
24252         Roo.each(this.panes, function(p) {
24253             if(p.el.hasClass('active')){
24254                 r = p;
24255                 return false;
24256             }
24257             
24258             return;
24259         });
24260         
24261         return r;
24262     }
24263     
24264     
24265 });
24266
24267  
24268 /*
24269  * - LGPL
24270  *
24271  * Tab pane
24272  * 
24273  */
24274 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24275 /**
24276  * @class Roo.bootstrap.TabPane
24277  * @extends Roo.bootstrap.Component
24278  * Bootstrap TabPane class
24279  * @cfg {Boolean} active (false | true) Default false
24280  * @cfg {String} title title of panel
24281
24282  * 
24283  * @constructor
24284  * Create a new TabPane
24285  * @param {Object} config The config object
24286  */
24287
24288 Roo.bootstrap.dash.TabPane = function(config){
24289     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
24290     
24291     this.addEvents({
24292         // raw events
24293         /**
24294          * @event activate
24295          * When a pane is activated
24296          * @param {Roo.bootstrap.dash.TabPane} pane
24297          */
24298         "activate" : true
24299          
24300     });
24301 };
24302
24303 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
24304     
24305     active : false,
24306     title : '',
24307     
24308     // the tabBox that this is attached to.
24309     tab : false,
24310      
24311     getAutoCreate : function() 
24312     {
24313         var cfg = {
24314             tag: 'div',
24315             cls: 'tab-pane'
24316         };
24317         
24318         if(this.active){
24319             cfg.cls += ' active';
24320         }
24321         
24322         return cfg;
24323     },
24324     initEvents  : function()
24325     {
24326         //Roo.log('trigger add pane handler');
24327         this.parent().fireEvent('addpane', this)
24328     },
24329     
24330      /**
24331      * Updates the tab title 
24332      * @param {String} html to set the title to.
24333      */
24334     setTitle: function(str)
24335     {
24336         if (!this.tab) {
24337             return;
24338         }
24339         this.title = str;
24340         this.tab.select('a', true).first().dom.innerHTML = str;
24341         
24342     }
24343     
24344     
24345     
24346 });
24347
24348  
24349
24350
24351  /*
24352  * - LGPL
24353  *
24354  * menu
24355  * 
24356  */
24357 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24358
24359 /**
24360  * @class Roo.bootstrap.menu.Menu
24361  * @extends Roo.bootstrap.Component
24362  * Bootstrap Menu class - container for Menu
24363  * @cfg {String} html Text of the menu
24364  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
24365  * @cfg {String} icon Font awesome icon
24366  * @cfg {String} pos Menu align to (top | bottom) default bottom
24367  * 
24368  * 
24369  * @constructor
24370  * Create a new Menu
24371  * @param {Object} config The config object
24372  */
24373
24374
24375 Roo.bootstrap.menu.Menu = function(config){
24376     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
24377     
24378     this.addEvents({
24379         /**
24380          * @event beforeshow
24381          * Fires before this menu is displayed
24382          * @param {Roo.bootstrap.menu.Menu} this
24383          */
24384         beforeshow : true,
24385         /**
24386          * @event beforehide
24387          * Fires before this menu is hidden
24388          * @param {Roo.bootstrap.menu.Menu} this
24389          */
24390         beforehide : true,
24391         /**
24392          * @event show
24393          * Fires after this menu is displayed
24394          * @param {Roo.bootstrap.menu.Menu} this
24395          */
24396         show : true,
24397         /**
24398          * @event hide
24399          * Fires after this menu is hidden
24400          * @param {Roo.bootstrap.menu.Menu} this
24401          */
24402         hide : true,
24403         /**
24404          * @event click
24405          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24406          * @param {Roo.bootstrap.menu.Menu} this
24407          * @param {Roo.EventObject} e
24408          */
24409         click : true
24410     });
24411     
24412 };
24413
24414 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
24415     
24416     submenu : false,
24417     html : '',
24418     weight : 'default',
24419     icon : false,
24420     pos : 'bottom',
24421     
24422     
24423     getChildContainer : function() {
24424         if(this.isSubMenu){
24425             return this.el;
24426         }
24427         
24428         return this.el.select('ul.dropdown-menu', true).first();  
24429     },
24430     
24431     getAutoCreate : function()
24432     {
24433         var text = [
24434             {
24435                 tag : 'span',
24436                 cls : 'roo-menu-text',
24437                 html : this.html
24438             }
24439         ];
24440         
24441         if(this.icon){
24442             text.unshift({
24443                 tag : 'i',
24444                 cls : 'fa ' + this.icon
24445             })
24446         }
24447         
24448         
24449         var cfg = {
24450             tag : 'div',
24451             cls : 'btn-group',
24452             cn : [
24453                 {
24454                     tag : 'button',
24455                     cls : 'dropdown-button btn btn-' + this.weight,
24456                     cn : text
24457                 },
24458                 {
24459                     tag : 'button',
24460                     cls : 'dropdown-toggle btn btn-' + this.weight,
24461                     cn : [
24462                         {
24463                             tag : 'span',
24464                             cls : 'caret'
24465                         }
24466                     ]
24467                 },
24468                 {
24469                     tag : 'ul',
24470                     cls : 'dropdown-menu'
24471                 }
24472             ]
24473             
24474         };
24475         
24476         if(this.pos == 'top'){
24477             cfg.cls += ' dropup';
24478         }
24479         
24480         if(this.isSubMenu){
24481             cfg = {
24482                 tag : 'ul',
24483                 cls : 'dropdown-menu'
24484             }
24485         }
24486         
24487         return cfg;
24488     },
24489     
24490     onRender : function(ct, position)
24491     {
24492         this.isSubMenu = ct.hasClass('dropdown-submenu');
24493         
24494         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24495     },
24496     
24497     initEvents : function() 
24498     {
24499         if(this.isSubMenu){
24500             return;
24501         }
24502         
24503         this.hidden = true;
24504         
24505         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24506         this.triggerEl.on('click', this.onTriggerPress, this);
24507         
24508         this.buttonEl = this.el.select('button.dropdown-button', true).first();
24509         this.buttonEl.on('click', this.onClick, this);
24510         
24511     },
24512     
24513     list : function()
24514     {
24515         if(this.isSubMenu){
24516             return this.el;
24517         }
24518         
24519         return this.el.select('ul.dropdown-menu', true).first();
24520     },
24521     
24522     onClick : function(e)
24523     {
24524         this.fireEvent("click", this, e);
24525     },
24526     
24527     onTriggerPress  : function(e)
24528     {   
24529         if (this.isVisible()) {
24530             this.hide();
24531         } else {
24532             this.show();
24533         }
24534     },
24535     
24536     isVisible : function(){
24537         return !this.hidden;
24538     },
24539     
24540     show : function()
24541     {
24542         this.fireEvent("beforeshow", this);
24543         
24544         this.hidden = false;
24545         this.el.addClass('open');
24546         
24547         Roo.get(document).on("mouseup", this.onMouseUp, this);
24548         
24549         this.fireEvent("show", this);
24550         
24551         
24552     },
24553     
24554     hide : function()
24555     {
24556         this.fireEvent("beforehide", this);
24557         
24558         this.hidden = true;
24559         this.el.removeClass('open');
24560         
24561         Roo.get(document).un("mouseup", this.onMouseUp);
24562         
24563         this.fireEvent("hide", this);
24564     },
24565     
24566     onMouseUp : function()
24567     {
24568         this.hide();
24569     }
24570     
24571 });
24572
24573  
24574  /*
24575  * - LGPL
24576  *
24577  * menu item
24578  * 
24579  */
24580 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24581
24582 /**
24583  * @class Roo.bootstrap.menu.Item
24584  * @extends Roo.bootstrap.Component
24585  * Bootstrap MenuItem class
24586  * @cfg {Boolean} submenu (true | false) default false
24587  * @cfg {String} html text of the item
24588  * @cfg {String} href the link
24589  * @cfg {Boolean} disable (true | false) default false
24590  * @cfg {Boolean} preventDefault (true | false) default true
24591  * @cfg {String} icon Font awesome icon
24592  * @cfg {String} pos Submenu align to (left | right) default right 
24593  * 
24594  * 
24595  * @constructor
24596  * Create a new Item
24597  * @param {Object} config The config object
24598  */
24599
24600
24601 Roo.bootstrap.menu.Item = function(config){
24602     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24603     this.addEvents({
24604         /**
24605          * @event mouseover
24606          * Fires when the mouse is hovering over this menu
24607          * @param {Roo.bootstrap.menu.Item} this
24608          * @param {Roo.EventObject} e
24609          */
24610         mouseover : true,
24611         /**
24612          * @event mouseout
24613          * Fires when the mouse exits this menu
24614          * @param {Roo.bootstrap.menu.Item} this
24615          * @param {Roo.EventObject} e
24616          */
24617         mouseout : true,
24618         // raw events
24619         /**
24620          * @event click
24621          * The raw click event for the entire grid.
24622          * @param {Roo.EventObject} e
24623          */
24624         click : true
24625     });
24626 };
24627
24628 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
24629     
24630     submenu : false,
24631     href : '',
24632     html : '',
24633     preventDefault: true,
24634     disable : false,
24635     icon : false,
24636     pos : 'right',
24637     
24638     getAutoCreate : function()
24639     {
24640         var text = [
24641             {
24642                 tag : 'span',
24643                 cls : 'roo-menu-item-text',
24644                 html : this.html
24645             }
24646         ];
24647         
24648         if(this.icon){
24649             text.unshift({
24650                 tag : 'i',
24651                 cls : 'fa ' + this.icon
24652             })
24653         }
24654         
24655         var cfg = {
24656             tag : 'li',
24657             cn : [
24658                 {
24659                     tag : 'a',
24660                     href : this.href || '#',
24661                     cn : text
24662                 }
24663             ]
24664         };
24665         
24666         if(this.disable){
24667             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24668         }
24669         
24670         if(this.submenu){
24671             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24672             
24673             if(this.pos == 'left'){
24674                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24675             }
24676         }
24677         
24678         return cfg;
24679     },
24680     
24681     initEvents : function() 
24682     {
24683         this.el.on('mouseover', this.onMouseOver, this);
24684         this.el.on('mouseout', this.onMouseOut, this);
24685         
24686         this.el.select('a', true).first().on('click', this.onClick, this);
24687         
24688     },
24689     
24690     onClick : function(e)
24691     {
24692         if(this.preventDefault){
24693             e.preventDefault();
24694         }
24695         
24696         this.fireEvent("click", this, e);
24697     },
24698     
24699     onMouseOver : function(e)
24700     {
24701         if(this.submenu && this.pos == 'left'){
24702             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24703         }
24704         
24705         this.fireEvent("mouseover", this, e);
24706     },
24707     
24708     onMouseOut : function(e)
24709     {
24710         this.fireEvent("mouseout", this, e);
24711     }
24712 });
24713
24714  
24715
24716  /*
24717  * - LGPL
24718  *
24719  * menu separator
24720  * 
24721  */
24722 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24723
24724 /**
24725  * @class Roo.bootstrap.menu.Separator
24726  * @extends Roo.bootstrap.Component
24727  * Bootstrap Separator class
24728  * 
24729  * @constructor
24730  * Create a new Separator
24731  * @param {Object} config The config object
24732  */
24733
24734
24735 Roo.bootstrap.menu.Separator = function(config){
24736     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24737 };
24738
24739 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
24740     
24741     getAutoCreate : function(){
24742         var cfg = {
24743             tag : 'li',
24744             cls: 'divider'
24745         };
24746         
24747         return cfg;
24748     }
24749    
24750 });
24751
24752  
24753
24754  /*
24755  * - LGPL
24756  *
24757  * Tooltip
24758  * 
24759  */
24760
24761 /**
24762  * @class Roo.bootstrap.Tooltip
24763  * Bootstrap Tooltip class
24764  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24765  * to determine which dom element triggers the tooltip.
24766  * 
24767  * It needs to add support for additional attributes like tooltip-position
24768  * 
24769  * @constructor
24770  * Create a new Toolti
24771  * @param {Object} config The config object
24772  */
24773
24774 Roo.bootstrap.Tooltip = function(config){
24775     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24776 };
24777
24778 Roo.apply(Roo.bootstrap.Tooltip, {
24779     /**
24780      * @function init initialize tooltip monitoring.
24781      * @static
24782      */
24783     currentEl : false,
24784     currentTip : false,
24785     currentRegion : false,
24786     
24787     //  init : delay?
24788     
24789     init : function()
24790     {
24791         Roo.get(document).on('mouseover', this.enter ,this);
24792         Roo.get(document).on('mouseout', this.leave, this);
24793          
24794         
24795         this.currentTip = new Roo.bootstrap.Tooltip();
24796     },
24797     
24798     enter : function(ev)
24799     {
24800         var dom = ev.getTarget();
24801         
24802         //Roo.log(['enter',dom]);
24803         var el = Roo.fly(dom);
24804         if (this.currentEl) {
24805             //Roo.log(dom);
24806             //Roo.log(this.currentEl);
24807             //Roo.log(this.currentEl.contains(dom));
24808             if (this.currentEl == el) {
24809                 return;
24810             }
24811             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24812                 return;
24813             }
24814
24815         }
24816         
24817         if (this.currentTip.el) {
24818             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24819         }    
24820         //Roo.log(ev);
24821         
24822         if(!el || el.dom == document){
24823             return;
24824         }
24825         
24826         var bindEl = el;
24827         
24828         // you can not look for children, as if el is the body.. then everythign is the child..
24829         if (!el.attr('tooltip')) { //
24830             if (!el.select("[tooltip]").elements.length) {
24831                 return;
24832             }
24833             // is the mouse over this child...?
24834             bindEl = el.select("[tooltip]").first();
24835             var xy = ev.getXY();
24836             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24837                 //Roo.log("not in region.");
24838                 return;
24839             }
24840             //Roo.log("child element over..");
24841             
24842         }
24843         this.currentEl = bindEl;
24844         this.currentTip.bind(bindEl);
24845         this.currentRegion = Roo.lib.Region.getRegion(dom);
24846         this.currentTip.enter();
24847         
24848     },
24849     leave : function(ev)
24850     {
24851         var dom = ev.getTarget();
24852         //Roo.log(['leave',dom]);
24853         if (!this.currentEl) {
24854             return;
24855         }
24856         
24857         
24858         if (dom != this.currentEl.dom) {
24859             return;
24860         }
24861         var xy = ev.getXY();
24862         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
24863             return;
24864         }
24865         // only activate leave if mouse cursor is outside... bounding box..
24866         
24867         
24868         
24869         
24870         if (this.currentTip) {
24871             this.currentTip.leave();
24872         }
24873         //Roo.log('clear currentEl');
24874         this.currentEl = false;
24875         
24876         
24877     },
24878     alignment : {
24879         'left' : ['r-l', [-2,0], 'right'],
24880         'right' : ['l-r', [2,0], 'left'],
24881         'bottom' : ['t-b', [0,2], 'top'],
24882         'top' : [ 'b-t', [0,-2], 'bottom']
24883     }
24884     
24885 });
24886
24887
24888 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
24889     
24890     
24891     bindEl : false,
24892     
24893     delay : null, // can be { show : 300 , hide: 500}
24894     
24895     timeout : null,
24896     
24897     hoverState : null, //???
24898     
24899     placement : 'bottom', 
24900     
24901     getAutoCreate : function(){
24902     
24903         var cfg = {
24904            cls : 'tooltip',
24905            role : 'tooltip',
24906            cn : [
24907                 {
24908                     cls : 'tooltip-arrow'
24909                 },
24910                 {
24911                     cls : 'tooltip-inner'
24912                 }
24913            ]
24914         };
24915         
24916         return cfg;
24917     },
24918     bind : function(el)
24919     {
24920         this.bindEl = el;
24921     },
24922       
24923     
24924     enter : function () {
24925        
24926         if (this.timeout != null) {
24927             clearTimeout(this.timeout);
24928         }
24929         
24930         this.hoverState = 'in';
24931          //Roo.log("enter - show");
24932         if (!this.delay || !this.delay.show) {
24933             this.show();
24934             return;
24935         }
24936         var _t = this;
24937         this.timeout = setTimeout(function () {
24938             if (_t.hoverState == 'in') {
24939                 _t.show();
24940             }
24941         }, this.delay.show);
24942     },
24943     leave : function()
24944     {
24945         clearTimeout(this.timeout);
24946     
24947         this.hoverState = 'out';
24948          if (!this.delay || !this.delay.hide) {
24949             this.hide();
24950             return;
24951         }
24952        
24953         var _t = this;
24954         this.timeout = setTimeout(function () {
24955             //Roo.log("leave - timeout");
24956             
24957             if (_t.hoverState == 'out') {
24958                 _t.hide();
24959                 Roo.bootstrap.Tooltip.currentEl = false;
24960             }
24961         }, delay);
24962     },
24963     
24964     show : function ()
24965     {
24966         if (!this.el) {
24967             this.render(document.body);
24968         }
24969         // set content.
24970         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24971         
24972         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24973         
24974         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24975         
24976         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24977         
24978         var placement = typeof this.placement == 'function' ?
24979             this.placement.call(this, this.el, on_el) :
24980             this.placement;
24981             
24982         var autoToken = /\s?auto?\s?/i;
24983         var autoPlace = autoToken.test(placement);
24984         if (autoPlace) {
24985             placement = placement.replace(autoToken, '') || 'top';
24986         }
24987         
24988         //this.el.detach()
24989         //this.el.setXY([0,0]);
24990         this.el.show();
24991         //this.el.dom.style.display='block';
24992         
24993         //this.el.appendTo(on_el);
24994         
24995         var p = this.getPosition();
24996         var box = this.el.getBox();
24997         
24998         if (autoPlace) {
24999             // fixme..
25000         }
25001         
25002         var align = Roo.bootstrap.Tooltip.alignment[placement];
25003         
25004         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25005         
25006         if(placement == 'top' || placement == 'bottom'){
25007             if(xy[0] < 0){
25008                 placement = 'right';
25009             }
25010             
25011             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25012                 placement = 'left';
25013             }
25014             
25015             var scroll = Roo.select('body', true).first().getScroll();
25016             
25017             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25018                 placement = 'top';
25019             }
25020             
25021         }
25022         
25023         align = Roo.bootstrap.Tooltip.alignment[placement];
25024         
25025         this.el.alignTo(this.bindEl, align[0],align[1]);
25026         //var arrow = this.el.select('.arrow',true).first();
25027         //arrow.set(align[2], 
25028         
25029         this.el.addClass(placement);
25030         
25031         this.el.addClass('in fade');
25032         
25033         this.hoverState = null;
25034         
25035         if (this.el.hasClass('fade')) {
25036             // fade it?
25037         }
25038         
25039     },
25040     hide : function()
25041     {
25042          
25043         if (!this.el) {
25044             return;
25045         }
25046         //this.el.setXY([0,0]);
25047         this.el.removeClass('in');
25048         //this.el.hide();
25049         
25050     }
25051     
25052 });
25053  
25054
25055  /*
25056  * - LGPL
25057  *
25058  * Location Picker
25059  * 
25060  */
25061
25062 /**
25063  * @class Roo.bootstrap.LocationPicker
25064  * @extends Roo.bootstrap.Component
25065  * Bootstrap LocationPicker class
25066  * @cfg {Number} latitude Position when init default 0
25067  * @cfg {Number} longitude Position when init default 0
25068  * @cfg {Number} zoom default 15
25069  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25070  * @cfg {Boolean} mapTypeControl default false
25071  * @cfg {Boolean} disableDoubleClickZoom default false
25072  * @cfg {Boolean} scrollwheel default true
25073  * @cfg {Boolean} streetViewControl default false
25074  * @cfg {Number} radius default 0
25075  * @cfg {String} locationName
25076  * @cfg {Boolean} draggable default true
25077  * @cfg {Boolean} enableAutocomplete default false
25078  * @cfg {Boolean} enableReverseGeocode default true
25079  * @cfg {String} markerTitle
25080  * 
25081  * @constructor
25082  * Create a new LocationPicker
25083  * @param {Object} config The config object
25084  */
25085
25086
25087 Roo.bootstrap.LocationPicker = function(config){
25088     
25089     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25090     
25091     this.addEvents({
25092         /**
25093          * @event initial
25094          * Fires when the picker initialized.
25095          * @param {Roo.bootstrap.LocationPicker} this
25096          * @param {Google Location} location
25097          */
25098         initial : true,
25099         /**
25100          * @event positionchanged
25101          * Fires when the picker position changed.
25102          * @param {Roo.bootstrap.LocationPicker} this
25103          * @param {Google Location} location
25104          */
25105         positionchanged : true,
25106         /**
25107          * @event resize
25108          * Fires when the map resize.
25109          * @param {Roo.bootstrap.LocationPicker} this
25110          */
25111         resize : true,
25112         /**
25113          * @event show
25114          * Fires when the map show.
25115          * @param {Roo.bootstrap.LocationPicker} this
25116          */
25117         show : true,
25118         /**
25119          * @event hide
25120          * Fires when the map hide.
25121          * @param {Roo.bootstrap.LocationPicker} this
25122          */
25123         hide : true,
25124         /**
25125          * @event mapClick
25126          * Fires when click the map.
25127          * @param {Roo.bootstrap.LocationPicker} this
25128          * @param {Map event} e
25129          */
25130         mapClick : true,
25131         /**
25132          * @event mapRightClick
25133          * Fires when right click the map.
25134          * @param {Roo.bootstrap.LocationPicker} this
25135          * @param {Map event} e
25136          */
25137         mapRightClick : true,
25138         /**
25139          * @event markerClick
25140          * Fires when click the marker.
25141          * @param {Roo.bootstrap.LocationPicker} this
25142          * @param {Map event} e
25143          */
25144         markerClick : true,
25145         /**
25146          * @event markerRightClick
25147          * Fires when right click the marker.
25148          * @param {Roo.bootstrap.LocationPicker} this
25149          * @param {Map event} e
25150          */
25151         markerRightClick : true,
25152         /**
25153          * @event OverlayViewDraw
25154          * Fires when OverlayView Draw
25155          * @param {Roo.bootstrap.LocationPicker} this
25156          */
25157         OverlayViewDraw : true,
25158         /**
25159          * @event OverlayViewOnAdd
25160          * Fires when OverlayView Draw
25161          * @param {Roo.bootstrap.LocationPicker} this
25162          */
25163         OverlayViewOnAdd : true,
25164         /**
25165          * @event OverlayViewOnRemove
25166          * Fires when OverlayView Draw
25167          * @param {Roo.bootstrap.LocationPicker} this
25168          */
25169         OverlayViewOnRemove : true,
25170         /**
25171          * @event OverlayViewShow
25172          * Fires when OverlayView Draw
25173          * @param {Roo.bootstrap.LocationPicker} this
25174          * @param {Pixel} cpx
25175          */
25176         OverlayViewShow : true,
25177         /**
25178          * @event OverlayViewHide
25179          * Fires when OverlayView Draw
25180          * @param {Roo.bootstrap.LocationPicker} this
25181          */
25182         OverlayViewHide : true,
25183         /**
25184          * @event loadexception
25185          * Fires when load google lib failed.
25186          * @param {Roo.bootstrap.LocationPicker} this
25187          */
25188         loadexception : true
25189     });
25190         
25191 };
25192
25193 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
25194     
25195     gMapContext: false,
25196     
25197     latitude: 0,
25198     longitude: 0,
25199     zoom: 15,
25200     mapTypeId: false,
25201     mapTypeControl: false,
25202     disableDoubleClickZoom: false,
25203     scrollwheel: true,
25204     streetViewControl: false,
25205     radius: 0,
25206     locationName: '',
25207     draggable: true,
25208     enableAutocomplete: false,
25209     enableReverseGeocode: true,
25210     markerTitle: '',
25211     
25212     getAutoCreate: function()
25213     {
25214
25215         var cfg = {
25216             tag: 'div',
25217             cls: 'roo-location-picker'
25218         };
25219         
25220         return cfg
25221     },
25222     
25223     initEvents: function(ct, position)
25224     {       
25225         if(!this.el.getWidth() || this.isApplied()){
25226             return;
25227         }
25228         
25229         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25230         
25231         this.initial();
25232     },
25233     
25234     initial: function()
25235     {
25236         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
25237             this.fireEvent('loadexception', this);
25238             return;
25239         }
25240         
25241         if(!this.mapTypeId){
25242             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
25243         }
25244         
25245         this.gMapContext = this.GMapContext();
25246         
25247         this.initOverlayView();
25248         
25249         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
25250         
25251         var _this = this;
25252                 
25253         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
25254             _this.setPosition(_this.gMapContext.marker.position);
25255         });
25256         
25257         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
25258             _this.fireEvent('mapClick', this, event);
25259             
25260         });
25261
25262         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
25263             _this.fireEvent('mapRightClick', this, event);
25264             
25265         });
25266         
25267         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
25268             _this.fireEvent('markerClick', this, event);
25269             
25270         });
25271
25272         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
25273             _this.fireEvent('markerRightClick', this, event);
25274             
25275         });
25276         
25277         this.setPosition(this.gMapContext.location);
25278         
25279         this.fireEvent('initial', this, this.gMapContext.location);
25280     },
25281     
25282     initOverlayView: function()
25283     {
25284         var _this = this;
25285         
25286         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
25287             
25288             draw: function()
25289             {
25290                 _this.fireEvent('OverlayViewDraw', _this);
25291             },
25292             
25293             onAdd: function()
25294             {
25295                 _this.fireEvent('OverlayViewOnAdd', _this);
25296             },
25297             
25298             onRemove: function()
25299             {
25300                 _this.fireEvent('OverlayViewOnRemove', _this);
25301             },
25302             
25303             show: function(cpx)
25304             {
25305                 _this.fireEvent('OverlayViewShow', _this, cpx);
25306             },
25307             
25308             hide: function()
25309             {
25310                 _this.fireEvent('OverlayViewHide', _this);
25311             }
25312             
25313         });
25314     },
25315     
25316     fromLatLngToContainerPixel: function(event)
25317     {
25318         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
25319     },
25320     
25321     isApplied: function() 
25322     {
25323         return this.getGmapContext() == false ? false : true;
25324     },
25325     
25326     getGmapContext: function() 
25327     {
25328         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
25329     },
25330     
25331     GMapContext: function() 
25332     {
25333         var position = new google.maps.LatLng(this.latitude, this.longitude);
25334         
25335         var _map = new google.maps.Map(this.el.dom, {
25336             center: position,
25337             zoom: this.zoom,
25338             mapTypeId: this.mapTypeId,
25339             mapTypeControl: this.mapTypeControl,
25340             disableDoubleClickZoom: this.disableDoubleClickZoom,
25341             scrollwheel: this.scrollwheel,
25342             streetViewControl: this.streetViewControl,
25343             locationName: this.locationName,
25344             draggable: this.draggable,
25345             enableAutocomplete: this.enableAutocomplete,
25346             enableReverseGeocode: this.enableReverseGeocode
25347         });
25348         
25349         var _marker = new google.maps.Marker({
25350             position: position,
25351             map: _map,
25352             title: this.markerTitle,
25353             draggable: this.draggable
25354         });
25355         
25356         return {
25357             map: _map,
25358             marker: _marker,
25359             circle: null,
25360             location: position,
25361             radius: this.radius,
25362             locationName: this.locationName,
25363             addressComponents: {
25364                 formatted_address: null,
25365                 addressLine1: null,
25366                 addressLine2: null,
25367                 streetName: null,
25368                 streetNumber: null,
25369                 city: null,
25370                 district: null,
25371                 state: null,
25372                 stateOrProvince: null
25373             },
25374             settings: this,
25375             domContainer: this.el.dom,
25376             geodecoder: new google.maps.Geocoder()
25377         };
25378     },
25379     
25380     drawCircle: function(center, radius, options) 
25381     {
25382         if (this.gMapContext.circle != null) {
25383             this.gMapContext.circle.setMap(null);
25384         }
25385         if (radius > 0) {
25386             radius *= 1;
25387             options = Roo.apply({}, options, {
25388                 strokeColor: "#0000FF",
25389                 strokeOpacity: .35,
25390                 strokeWeight: 2,
25391                 fillColor: "#0000FF",
25392                 fillOpacity: .2
25393             });
25394             
25395             options.map = this.gMapContext.map;
25396             options.radius = radius;
25397             options.center = center;
25398             this.gMapContext.circle = new google.maps.Circle(options);
25399             return this.gMapContext.circle;
25400         }
25401         
25402         return null;
25403     },
25404     
25405     setPosition: function(location) 
25406     {
25407         this.gMapContext.location = location;
25408         this.gMapContext.marker.setPosition(location);
25409         this.gMapContext.map.panTo(location);
25410         this.drawCircle(location, this.gMapContext.radius, {});
25411         
25412         var _this = this;
25413         
25414         if (this.gMapContext.settings.enableReverseGeocode) {
25415             this.gMapContext.geodecoder.geocode({
25416                 latLng: this.gMapContext.location
25417             }, function(results, status) {
25418                 
25419                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25420                     _this.gMapContext.locationName = results[0].formatted_address;
25421                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25422                     
25423                     _this.fireEvent('positionchanged', this, location);
25424                 }
25425             });
25426             
25427             return;
25428         }
25429         
25430         this.fireEvent('positionchanged', this, location);
25431     },
25432     
25433     resize: function()
25434     {
25435         google.maps.event.trigger(this.gMapContext.map, "resize");
25436         
25437         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25438         
25439         this.fireEvent('resize', this);
25440     },
25441     
25442     setPositionByLatLng: function(latitude, longitude)
25443     {
25444         this.setPosition(new google.maps.LatLng(latitude, longitude));
25445     },
25446     
25447     getCurrentPosition: function() 
25448     {
25449         return {
25450             latitude: this.gMapContext.location.lat(),
25451             longitude: this.gMapContext.location.lng()
25452         };
25453     },
25454     
25455     getAddressName: function() 
25456     {
25457         return this.gMapContext.locationName;
25458     },
25459     
25460     getAddressComponents: function() 
25461     {
25462         return this.gMapContext.addressComponents;
25463     },
25464     
25465     address_component_from_google_geocode: function(address_components) 
25466     {
25467         var result = {};
25468         
25469         for (var i = 0; i < address_components.length; i++) {
25470             var component = address_components[i];
25471             if (component.types.indexOf("postal_code") >= 0) {
25472                 result.postalCode = component.short_name;
25473             } else if (component.types.indexOf("street_number") >= 0) {
25474                 result.streetNumber = component.short_name;
25475             } else if (component.types.indexOf("route") >= 0) {
25476                 result.streetName = component.short_name;
25477             } else if (component.types.indexOf("neighborhood") >= 0) {
25478                 result.city = component.short_name;
25479             } else if (component.types.indexOf("locality") >= 0) {
25480                 result.city = component.short_name;
25481             } else if (component.types.indexOf("sublocality") >= 0) {
25482                 result.district = component.short_name;
25483             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25484                 result.stateOrProvince = component.short_name;
25485             } else if (component.types.indexOf("country") >= 0) {
25486                 result.country = component.short_name;
25487             }
25488         }
25489         
25490         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25491         result.addressLine2 = "";
25492         return result;
25493     },
25494     
25495     setZoomLevel: function(zoom)
25496     {
25497         this.gMapContext.map.setZoom(zoom);
25498     },
25499     
25500     show: function()
25501     {
25502         if(!this.el){
25503             return;
25504         }
25505         
25506         this.el.show();
25507         
25508         this.resize();
25509         
25510         this.fireEvent('show', this);
25511     },
25512     
25513     hide: function()
25514     {
25515         if(!this.el){
25516             return;
25517         }
25518         
25519         this.el.hide();
25520         
25521         this.fireEvent('hide', this);
25522     }
25523     
25524 });
25525
25526 Roo.apply(Roo.bootstrap.LocationPicker, {
25527     
25528     OverlayView : function(map, options)
25529     {
25530         options = options || {};
25531         
25532         this.setMap(map);
25533     }
25534     
25535     
25536 });/*
25537  * - LGPL
25538  *
25539  * Alert
25540  * 
25541  */
25542
25543 /**
25544  * @class Roo.bootstrap.Alert
25545  * @extends Roo.bootstrap.Component
25546  * Bootstrap Alert class
25547  * @cfg {String} title The title of alert
25548  * @cfg {String} html The content of alert
25549  * @cfg {String} weight (  success | info | warning | danger )
25550  * @cfg {String} faicon font-awesomeicon
25551  * 
25552  * @constructor
25553  * Create a new alert
25554  * @param {Object} config The config object
25555  */
25556
25557
25558 Roo.bootstrap.Alert = function(config){
25559     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25560     
25561 };
25562
25563 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
25564     
25565     title: '',
25566     html: '',
25567     weight: false,
25568     faicon: false,
25569     
25570     getAutoCreate : function()
25571     {
25572         
25573         var cfg = {
25574             tag : 'div',
25575             cls : 'alert',
25576             cn : [
25577                 {
25578                     tag : 'i',
25579                     cls : 'roo-alert-icon'
25580                     
25581                 },
25582                 {
25583                     tag : 'b',
25584                     cls : 'roo-alert-title',
25585                     html : this.title
25586                 },
25587                 {
25588                     tag : 'span',
25589                     cls : 'roo-alert-text',
25590                     html : this.html
25591                 }
25592             ]
25593         };
25594         
25595         if(this.faicon){
25596             cfg.cn[0].cls += ' fa ' + this.faicon;
25597         }
25598         
25599         if(this.weight){
25600             cfg.cls += ' alert-' + this.weight;
25601         }
25602         
25603         return cfg;
25604     },
25605     
25606     initEvents: function() 
25607     {
25608         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25609     },
25610     
25611     setTitle : function(str)
25612     {
25613         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25614     },
25615     
25616     setText : function(str)
25617     {
25618         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25619     },
25620     
25621     setWeight : function(weight)
25622     {
25623         if(this.weight){
25624             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25625         }
25626         
25627         this.weight = weight;
25628         
25629         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25630     },
25631     
25632     setIcon : function(icon)
25633     {
25634         if(this.faicon){
25635             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25636         }
25637         
25638         this.faicon = icon;
25639         
25640         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25641     },
25642     
25643     hide: function() 
25644     {
25645         this.el.hide();   
25646     },
25647     
25648     show: function() 
25649     {  
25650         this.el.show();   
25651     }
25652     
25653 });
25654
25655  
25656 /*
25657 * Licence: LGPL
25658 */
25659
25660 /**
25661  * @class Roo.bootstrap.UploadCropbox
25662  * @extends Roo.bootstrap.Component
25663  * Bootstrap UploadCropbox class
25664  * @cfg {String} emptyText show when image has been loaded
25665  * @cfg {String} rotateNotify show when image too small to rotate
25666  * @cfg {Number} errorTimeout default 3000
25667  * @cfg {Number} minWidth default 300
25668  * @cfg {Number} minHeight default 300
25669  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25670  * @cfg {Boolean} isDocument (true|false) default false
25671  * @cfg {String} url action url
25672  * @cfg {String} paramName default 'imageUpload'
25673  * @cfg {String} method default POST
25674  * @cfg {Boolean} loadMask (true|false) default true
25675  * @cfg {Boolean} loadingText default 'Loading...'
25676  * 
25677  * @constructor
25678  * Create a new UploadCropbox
25679  * @param {Object} config The config object
25680  */
25681
25682 Roo.bootstrap.UploadCropbox = function(config){
25683     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25684     
25685     this.addEvents({
25686         /**
25687          * @event beforeselectfile
25688          * Fire before select file
25689          * @param {Roo.bootstrap.UploadCropbox} this
25690          */
25691         "beforeselectfile" : true,
25692         /**
25693          * @event initial
25694          * Fire after initEvent
25695          * @param {Roo.bootstrap.UploadCropbox} this
25696          */
25697         "initial" : true,
25698         /**
25699          * @event crop
25700          * Fire after initEvent
25701          * @param {Roo.bootstrap.UploadCropbox} this
25702          * @param {String} data
25703          */
25704         "crop" : true,
25705         /**
25706          * @event prepare
25707          * Fire when preparing the file data
25708          * @param {Roo.bootstrap.UploadCropbox} this
25709          * @param {Object} file
25710          */
25711         "prepare" : true,
25712         /**
25713          * @event exception
25714          * Fire when get exception
25715          * @param {Roo.bootstrap.UploadCropbox} this
25716          * @param {XMLHttpRequest} xhr
25717          */
25718         "exception" : true,
25719         /**
25720          * @event beforeloadcanvas
25721          * Fire before load the canvas
25722          * @param {Roo.bootstrap.UploadCropbox} this
25723          * @param {String} src
25724          */
25725         "beforeloadcanvas" : true,
25726         /**
25727          * @event trash
25728          * Fire when trash image
25729          * @param {Roo.bootstrap.UploadCropbox} this
25730          */
25731         "trash" : true,
25732         /**
25733          * @event download
25734          * Fire when download the image
25735          * @param {Roo.bootstrap.UploadCropbox} this
25736          */
25737         "download" : true,
25738         /**
25739          * @event footerbuttonclick
25740          * Fire when footerbuttonclick
25741          * @param {Roo.bootstrap.UploadCropbox} this
25742          * @param {String} type
25743          */
25744         "footerbuttonclick" : true,
25745         /**
25746          * @event resize
25747          * Fire when resize
25748          * @param {Roo.bootstrap.UploadCropbox} this
25749          */
25750         "resize" : true,
25751         /**
25752          * @event rotate
25753          * Fire when rotate the image
25754          * @param {Roo.bootstrap.UploadCropbox} this
25755          * @param {String} pos
25756          */
25757         "rotate" : true,
25758         /**
25759          * @event inspect
25760          * Fire when inspect the file
25761          * @param {Roo.bootstrap.UploadCropbox} this
25762          * @param {Object} file
25763          */
25764         "inspect" : true,
25765         /**
25766          * @event upload
25767          * Fire when xhr upload the file
25768          * @param {Roo.bootstrap.UploadCropbox} this
25769          * @param {Object} data
25770          */
25771         "upload" : true,
25772         /**
25773          * @event arrange
25774          * Fire when arrange the file data
25775          * @param {Roo.bootstrap.UploadCropbox} this
25776          * @param {Object} formData
25777          */
25778         "arrange" : true
25779     });
25780     
25781     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25782 };
25783
25784 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
25785     
25786     emptyText : 'Click to upload image',
25787     rotateNotify : 'Image is too small to rotate',
25788     errorTimeout : 3000,
25789     scale : 0,
25790     baseScale : 1,
25791     rotate : 0,
25792     dragable : false,
25793     pinching : false,
25794     mouseX : 0,
25795     mouseY : 0,
25796     cropData : false,
25797     minWidth : 300,
25798     minHeight : 300,
25799     file : false,
25800     exif : {},
25801     baseRotate : 1,
25802     cropType : 'image/jpeg',
25803     buttons : false,
25804     canvasLoaded : false,
25805     isDocument : false,
25806     method : 'POST',
25807     paramName : 'imageUpload',
25808     loadMask : true,
25809     loadingText : 'Loading...',
25810     maskEl : false,
25811     
25812     getAutoCreate : function()
25813     {
25814         var cfg = {
25815             tag : 'div',
25816             cls : 'roo-upload-cropbox',
25817             cn : [
25818                 {
25819                     tag : 'input',
25820                     cls : 'roo-upload-cropbox-selector',
25821                     type : 'file'
25822                 },
25823                 {
25824                     tag : 'div',
25825                     cls : 'roo-upload-cropbox-body',
25826                     style : 'cursor:pointer',
25827                     cn : [
25828                         {
25829                             tag : 'div',
25830                             cls : 'roo-upload-cropbox-preview'
25831                         },
25832                         {
25833                             tag : 'div',
25834                             cls : 'roo-upload-cropbox-thumb'
25835                         },
25836                         {
25837                             tag : 'div',
25838                             cls : 'roo-upload-cropbox-empty-notify',
25839                             html : this.emptyText
25840                         },
25841                         {
25842                             tag : 'div',
25843                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25844                             html : this.rotateNotify
25845                         }
25846                     ]
25847                 },
25848                 {
25849                     tag : 'div',
25850                     cls : 'roo-upload-cropbox-footer',
25851                     cn : {
25852                         tag : 'div',
25853                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25854                         cn : []
25855                     }
25856                 }
25857             ]
25858         };
25859         
25860         return cfg;
25861     },
25862     
25863     onRender : function(ct, position)
25864     {
25865         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25866         
25867         if (this.buttons.length) {
25868             
25869             Roo.each(this.buttons, function(bb) {
25870                 
25871                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25872                 
25873                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25874                 
25875             }, this);
25876         }
25877         
25878         if(this.loadMask){
25879             this.maskEl = this.el;
25880         }
25881     },
25882     
25883     initEvents : function()
25884     {
25885         this.urlAPI = (window.createObjectURL && window) || 
25886                                 (window.URL && URL.revokeObjectURL && URL) || 
25887                                 (window.webkitURL && webkitURL);
25888                         
25889         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25890         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25891         
25892         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25893         this.selectorEl.hide();
25894         
25895         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25896         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25897         
25898         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25899         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25900         this.thumbEl.hide();
25901         
25902         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25903         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25904         
25905         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25906         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25907         this.errorEl.hide();
25908         
25909         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25910         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25911         this.footerEl.hide();
25912         
25913         this.setThumbBoxSize();
25914         
25915         this.bind();
25916         
25917         this.resize();
25918         
25919         this.fireEvent('initial', this);
25920     },
25921
25922     bind : function()
25923     {
25924         var _this = this;
25925         
25926         window.addEventListener("resize", function() { _this.resize(); } );
25927         
25928         this.bodyEl.on('click', this.beforeSelectFile, this);
25929         
25930         if(Roo.isTouch){
25931             this.bodyEl.on('touchstart', this.onTouchStart, this);
25932             this.bodyEl.on('touchmove', this.onTouchMove, this);
25933             this.bodyEl.on('touchend', this.onTouchEnd, this);
25934         }
25935         
25936         if(!Roo.isTouch){
25937             this.bodyEl.on('mousedown', this.onMouseDown, this);
25938             this.bodyEl.on('mousemove', this.onMouseMove, this);
25939             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25940             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25941             Roo.get(document).on('mouseup', this.onMouseUp, this);
25942         }
25943         
25944         this.selectorEl.on('change', this.onFileSelected, this);
25945     },
25946     
25947     reset : function()
25948     {    
25949         this.scale = 0;
25950         this.baseScale = 1;
25951         this.rotate = 0;
25952         this.baseRotate = 1;
25953         this.dragable = false;
25954         this.pinching = false;
25955         this.mouseX = 0;
25956         this.mouseY = 0;
25957         this.cropData = false;
25958         this.notifyEl.dom.innerHTML = this.emptyText;
25959         
25960         this.selectorEl.dom.value = '';
25961         
25962     },
25963     
25964     resize : function()
25965     {
25966         if(this.fireEvent('resize', this) != false){
25967             this.setThumbBoxPosition();
25968             this.setCanvasPosition();
25969         }
25970     },
25971     
25972     onFooterButtonClick : function(e, el, o, type)
25973     {
25974         switch (type) {
25975             case 'rotate-left' :
25976                 this.onRotateLeft(e);
25977                 break;
25978             case 'rotate-right' :
25979                 this.onRotateRight(e);
25980                 break;
25981             case 'picture' :
25982                 this.beforeSelectFile(e);
25983                 break;
25984             case 'trash' :
25985                 this.trash(e);
25986                 break;
25987             case 'crop' :
25988                 this.crop(e);
25989                 break;
25990             case 'download' :
25991                 this.download(e);
25992                 break;
25993             default :
25994                 break;
25995         }
25996         
25997         this.fireEvent('footerbuttonclick', this, type);
25998     },
25999     
26000     beforeSelectFile : function(e)
26001     {
26002         e.preventDefault();
26003         
26004         if(this.fireEvent('beforeselectfile', this) != false){
26005             this.selectorEl.dom.click();
26006         }
26007     },
26008     
26009     onFileSelected : function(e)
26010     {
26011         e.preventDefault();
26012         
26013         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26014             return;
26015         }
26016         
26017         var file = this.selectorEl.dom.files[0];
26018         
26019         if(this.fireEvent('inspect', this, file) != false){
26020             this.prepare(file);
26021         }
26022         
26023     },
26024     
26025     trash : function(e)
26026     {
26027         this.fireEvent('trash', this);
26028     },
26029     
26030     download : function(e)
26031     {
26032         this.fireEvent('download', this);
26033     },
26034     
26035     loadCanvas : function(src)
26036     {   
26037         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26038             
26039             this.reset();
26040             
26041             this.imageEl = document.createElement('img');
26042             
26043             var _this = this;
26044             
26045             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26046             
26047             this.imageEl.src = src;
26048         }
26049     },
26050     
26051     onLoadCanvas : function()
26052     {   
26053         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26054         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26055         
26056         this.bodyEl.un('click', this.beforeSelectFile, this);
26057         
26058         this.notifyEl.hide();
26059         this.thumbEl.show();
26060         this.footerEl.show();
26061         
26062         this.baseRotateLevel();
26063         
26064         if(this.isDocument){
26065             this.setThumbBoxSize();
26066         }
26067         
26068         this.setThumbBoxPosition();
26069         
26070         this.baseScaleLevel();
26071         
26072         this.draw();
26073         
26074         this.resize();
26075         
26076         this.canvasLoaded = true;
26077         
26078         if(this.loadMask){
26079             this.maskEl.unmask();
26080         }
26081         
26082     },
26083     
26084     setCanvasPosition : function()
26085     {   
26086         if(!this.canvasEl){
26087             return;
26088         }
26089         
26090         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26091         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26092         
26093         this.previewEl.setLeft(pw);
26094         this.previewEl.setTop(ph);
26095         
26096     },
26097     
26098     onMouseDown : function(e)
26099     {   
26100         e.stopEvent();
26101         
26102         this.dragable = true;
26103         this.pinching = false;
26104         
26105         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26106             this.dragable = false;
26107             return;
26108         }
26109         
26110         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26111         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26112         
26113     },
26114     
26115     onMouseMove : function(e)
26116     {   
26117         e.stopEvent();
26118         
26119         if(!this.canvasLoaded){
26120             return;
26121         }
26122         
26123         if (!this.dragable){
26124             return;
26125         }
26126         
26127         var minX = Math.ceil(this.thumbEl.getLeft(true));
26128         var minY = Math.ceil(this.thumbEl.getTop(true));
26129         
26130         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26131         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26132         
26133         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26134         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26135         
26136         x = x - this.mouseX;
26137         y = y - this.mouseY;
26138         
26139         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26140         var bgY = Math.ceil(y + this.previewEl.getTop(true));
26141         
26142         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26143         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26144         
26145         this.previewEl.setLeft(bgX);
26146         this.previewEl.setTop(bgY);
26147         
26148         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26149         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26150     },
26151     
26152     onMouseUp : function(e)
26153     {   
26154         e.stopEvent();
26155         
26156         this.dragable = false;
26157     },
26158     
26159     onMouseWheel : function(e)
26160     {   
26161         e.stopEvent();
26162         
26163         this.startScale = this.scale;
26164         
26165         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26166         
26167         if(!this.zoomable()){
26168             this.scale = this.startScale;
26169             return;
26170         }
26171         
26172         this.draw();
26173         
26174         return;
26175     },
26176     
26177     zoomable : function()
26178     {
26179         var minScale = this.thumbEl.getWidth() / this.minWidth;
26180         
26181         if(this.minWidth < this.minHeight){
26182             minScale = this.thumbEl.getHeight() / this.minHeight;
26183         }
26184         
26185         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26186         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26187         
26188         if(
26189                 this.isDocument &&
26190                 (this.rotate == 0 || this.rotate == 180) && 
26191                 (
26192                     width > this.imageEl.OriginWidth || 
26193                     height > this.imageEl.OriginHeight ||
26194                     (width < this.minWidth && height < this.minHeight)
26195                 )
26196         ){
26197             return false;
26198         }
26199         
26200         if(
26201                 this.isDocument &&
26202                 (this.rotate == 90 || this.rotate == 270) && 
26203                 (
26204                     width > this.imageEl.OriginWidth || 
26205                     height > this.imageEl.OriginHeight ||
26206                     (width < this.minHeight && height < this.minWidth)
26207                 )
26208         ){
26209             return false;
26210         }
26211         
26212         if(
26213                 !this.isDocument &&
26214                 (this.rotate == 0 || this.rotate == 180) && 
26215                 (
26216                     width < this.minWidth || 
26217                     width > this.imageEl.OriginWidth || 
26218                     height < this.minHeight || 
26219                     height > this.imageEl.OriginHeight
26220                 )
26221         ){
26222             return false;
26223         }
26224         
26225         if(
26226                 !this.isDocument &&
26227                 (this.rotate == 90 || this.rotate == 270) && 
26228                 (
26229                     width < this.minHeight || 
26230                     width > this.imageEl.OriginWidth || 
26231                     height < this.minWidth || 
26232                     height > this.imageEl.OriginHeight
26233                 )
26234         ){
26235             return false;
26236         }
26237         
26238         return true;
26239         
26240     },
26241     
26242     onRotateLeft : function(e)
26243     {   
26244         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26245             
26246             var minScale = this.thumbEl.getWidth() / this.minWidth;
26247             
26248             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26249             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26250             
26251             this.startScale = this.scale;
26252             
26253             while (this.getScaleLevel() < minScale){
26254             
26255                 this.scale = this.scale + 1;
26256                 
26257                 if(!this.zoomable()){
26258                     break;
26259                 }
26260                 
26261                 if(
26262                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26263                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26264                 ){
26265                     continue;
26266                 }
26267                 
26268                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26269
26270                 this.draw();
26271                 
26272                 return;
26273             }
26274             
26275             this.scale = this.startScale;
26276             
26277             this.onRotateFail();
26278             
26279             return false;
26280         }
26281         
26282         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26283
26284         if(this.isDocument){
26285             this.setThumbBoxSize();
26286             this.setThumbBoxPosition();
26287             this.setCanvasPosition();
26288         }
26289         
26290         this.draw();
26291         
26292         this.fireEvent('rotate', this, 'left');
26293         
26294     },
26295     
26296     onRotateRight : function(e)
26297     {
26298         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26299             
26300             var minScale = this.thumbEl.getWidth() / this.minWidth;
26301         
26302             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26303             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26304             
26305             this.startScale = this.scale;
26306             
26307             while (this.getScaleLevel() < minScale){
26308             
26309                 this.scale = this.scale + 1;
26310                 
26311                 if(!this.zoomable()){
26312                     break;
26313                 }
26314                 
26315                 if(
26316                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26317                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26318                 ){
26319                     continue;
26320                 }
26321                 
26322                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26323
26324                 this.draw();
26325                 
26326                 return;
26327             }
26328             
26329             this.scale = this.startScale;
26330             
26331             this.onRotateFail();
26332             
26333             return false;
26334         }
26335         
26336         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26337
26338         if(this.isDocument){
26339             this.setThumbBoxSize();
26340             this.setThumbBoxPosition();
26341             this.setCanvasPosition();
26342         }
26343         
26344         this.draw();
26345         
26346         this.fireEvent('rotate', this, 'right');
26347     },
26348     
26349     onRotateFail : function()
26350     {
26351         this.errorEl.show(true);
26352         
26353         var _this = this;
26354         
26355         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26356     },
26357     
26358     draw : function()
26359     {
26360         this.previewEl.dom.innerHTML = '';
26361         
26362         var canvasEl = document.createElement("canvas");
26363         
26364         var contextEl = canvasEl.getContext("2d");
26365         
26366         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26367         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26368         var center = this.imageEl.OriginWidth / 2;
26369         
26370         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26371             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26372             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26373             center = this.imageEl.OriginHeight / 2;
26374         }
26375         
26376         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26377         
26378         contextEl.translate(center, center);
26379         contextEl.rotate(this.rotate * Math.PI / 180);
26380
26381         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26382         
26383         this.canvasEl = document.createElement("canvas");
26384         
26385         this.contextEl = this.canvasEl.getContext("2d");
26386         
26387         switch (this.rotate) {
26388             case 0 :
26389                 
26390                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26391                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26392                 
26393                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26394                 
26395                 break;
26396             case 90 : 
26397                 
26398                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26399                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26400                 
26401                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26402                     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);
26403                     break;
26404                 }
26405                 
26406                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26407                 
26408                 break;
26409             case 180 :
26410                 
26411                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26412                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26413                 
26414                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26415                     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);
26416                     break;
26417                 }
26418                 
26419                 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);
26420                 
26421                 break;
26422             case 270 :
26423                 
26424                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26425                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26426         
26427                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26428                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26429                     break;
26430                 }
26431                 
26432                 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);
26433                 
26434                 break;
26435             default : 
26436                 break;
26437         }
26438         
26439         this.previewEl.appendChild(this.canvasEl);
26440         
26441         this.setCanvasPosition();
26442     },
26443     
26444     crop : function()
26445     {
26446         if(!this.canvasLoaded){
26447             return;
26448         }
26449         
26450         var imageCanvas = document.createElement("canvas");
26451         
26452         var imageContext = imageCanvas.getContext("2d");
26453         
26454         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26455         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26456         
26457         var center = imageCanvas.width / 2;
26458         
26459         imageContext.translate(center, center);
26460         
26461         imageContext.rotate(this.rotate * Math.PI / 180);
26462         
26463         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26464         
26465         var canvas = document.createElement("canvas");
26466         
26467         var context = canvas.getContext("2d");
26468                 
26469         canvas.width = this.minWidth;
26470         canvas.height = this.minHeight;
26471
26472         switch (this.rotate) {
26473             case 0 :
26474                 
26475                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26476                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26477                 
26478                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26479                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26480                 
26481                 var targetWidth = this.minWidth - 2 * x;
26482                 var targetHeight = this.minHeight - 2 * y;
26483                 
26484                 var scale = 1;
26485                 
26486                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26487                     scale = targetWidth / width;
26488                 }
26489                 
26490                 if(x > 0 && y == 0){
26491                     scale = targetHeight / height;
26492                 }
26493                 
26494                 if(x > 0 && y > 0){
26495                     scale = targetWidth / width;
26496                     
26497                     if(width < height){
26498                         scale = targetHeight / height;
26499                     }
26500                 }
26501                 
26502                 context.scale(scale, scale);
26503                 
26504                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26505                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26506
26507                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26508                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26509
26510                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26511                 
26512                 break;
26513             case 90 : 
26514                 
26515                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26516                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26517                 
26518                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26519                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26520                 
26521                 var targetWidth = this.minWidth - 2 * x;
26522                 var targetHeight = this.minHeight - 2 * y;
26523                 
26524                 var scale = 1;
26525                 
26526                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26527                     scale = targetWidth / width;
26528                 }
26529                 
26530                 if(x > 0 && y == 0){
26531                     scale = targetHeight / height;
26532                 }
26533                 
26534                 if(x > 0 && y > 0){
26535                     scale = targetWidth / width;
26536                     
26537                     if(width < height){
26538                         scale = targetHeight / height;
26539                     }
26540                 }
26541                 
26542                 context.scale(scale, scale);
26543                 
26544                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26545                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26546
26547                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26548                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26549                 
26550                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26551                 
26552                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26553                 
26554                 break;
26555             case 180 :
26556                 
26557                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26558                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26559                 
26560                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26561                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26562                 
26563                 var targetWidth = this.minWidth - 2 * x;
26564                 var targetHeight = this.minHeight - 2 * y;
26565                 
26566                 var scale = 1;
26567                 
26568                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26569                     scale = targetWidth / width;
26570                 }
26571                 
26572                 if(x > 0 && y == 0){
26573                     scale = targetHeight / height;
26574                 }
26575                 
26576                 if(x > 0 && y > 0){
26577                     scale = targetWidth / width;
26578                     
26579                     if(width < height){
26580                         scale = targetHeight / height;
26581                     }
26582                 }
26583                 
26584                 context.scale(scale, scale);
26585                 
26586                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26587                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26588
26589                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26590                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26591
26592                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26593                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26594                 
26595                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26596                 
26597                 break;
26598             case 270 :
26599                 
26600                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26601                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26602                 
26603                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26604                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26605                 
26606                 var targetWidth = this.minWidth - 2 * x;
26607                 var targetHeight = this.minHeight - 2 * y;
26608                 
26609                 var scale = 1;
26610                 
26611                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26612                     scale = targetWidth / width;
26613                 }
26614                 
26615                 if(x > 0 && y == 0){
26616                     scale = targetHeight / height;
26617                 }
26618                 
26619                 if(x > 0 && y > 0){
26620                     scale = targetWidth / width;
26621                     
26622                     if(width < height){
26623                         scale = targetHeight / height;
26624                     }
26625                 }
26626                 
26627                 context.scale(scale, scale);
26628                 
26629                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26630                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26631
26632                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26633                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26634                 
26635                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26636                 
26637                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26638                 
26639                 break;
26640             default : 
26641                 break;
26642         }
26643         
26644         this.cropData = canvas.toDataURL(this.cropType);
26645         
26646         if(this.fireEvent('crop', this, this.cropData) !== false){
26647             this.process(this.file, this.cropData);
26648         }
26649         
26650         return;
26651         
26652     },
26653     
26654     setThumbBoxSize : function()
26655     {
26656         var width, height;
26657         
26658         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26659             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26660             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26661             
26662             this.minWidth = width;
26663             this.minHeight = height;
26664             
26665             if(this.rotate == 90 || this.rotate == 270){
26666                 this.minWidth = height;
26667                 this.minHeight = width;
26668             }
26669         }
26670         
26671         height = 300;
26672         width = Math.ceil(this.minWidth * height / this.minHeight);
26673         
26674         if(this.minWidth > this.minHeight){
26675             width = 300;
26676             height = Math.ceil(this.minHeight * width / this.minWidth);
26677         }
26678         
26679         this.thumbEl.setStyle({
26680             width : width + 'px',
26681             height : height + 'px'
26682         });
26683
26684         return;
26685             
26686     },
26687     
26688     setThumbBoxPosition : function()
26689     {
26690         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26691         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26692         
26693         this.thumbEl.setLeft(x);
26694         this.thumbEl.setTop(y);
26695         
26696     },
26697     
26698     baseRotateLevel : function()
26699     {
26700         this.baseRotate = 1;
26701         
26702         if(
26703                 typeof(this.exif) != 'undefined' &&
26704                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26705                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26706         ){
26707             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26708         }
26709         
26710         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26711         
26712     },
26713     
26714     baseScaleLevel : function()
26715     {
26716         var width, height;
26717         
26718         if(this.isDocument){
26719             
26720             if(this.baseRotate == 6 || this.baseRotate == 8){
26721             
26722                 height = this.thumbEl.getHeight();
26723                 this.baseScale = height / this.imageEl.OriginWidth;
26724
26725                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26726                     width = this.thumbEl.getWidth();
26727                     this.baseScale = width / this.imageEl.OriginHeight;
26728                 }
26729
26730                 return;
26731             }
26732
26733             height = this.thumbEl.getHeight();
26734             this.baseScale = height / this.imageEl.OriginHeight;
26735
26736             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26737                 width = this.thumbEl.getWidth();
26738                 this.baseScale = width / this.imageEl.OriginWidth;
26739             }
26740
26741             return;
26742         }
26743         
26744         if(this.baseRotate == 6 || this.baseRotate == 8){
26745             
26746             width = this.thumbEl.getHeight();
26747             this.baseScale = width / this.imageEl.OriginHeight;
26748             
26749             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26750                 height = this.thumbEl.getWidth();
26751                 this.baseScale = height / this.imageEl.OriginHeight;
26752             }
26753             
26754             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26755                 height = this.thumbEl.getWidth();
26756                 this.baseScale = height / this.imageEl.OriginHeight;
26757                 
26758                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26759                     width = this.thumbEl.getHeight();
26760                     this.baseScale = width / this.imageEl.OriginWidth;
26761                 }
26762             }
26763             
26764             return;
26765         }
26766         
26767         width = this.thumbEl.getWidth();
26768         this.baseScale = width / this.imageEl.OriginWidth;
26769         
26770         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26771             height = this.thumbEl.getHeight();
26772             this.baseScale = height / this.imageEl.OriginHeight;
26773         }
26774         
26775         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26776             
26777             height = this.thumbEl.getHeight();
26778             this.baseScale = height / this.imageEl.OriginHeight;
26779             
26780             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26781                 width = this.thumbEl.getWidth();
26782                 this.baseScale = width / this.imageEl.OriginWidth;
26783             }
26784             
26785         }
26786         
26787         return;
26788     },
26789     
26790     getScaleLevel : function()
26791     {
26792         return this.baseScale * Math.pow(1.1, this.scale);
26793     },
26794     
26795     onTouchStart : function(e)
26796     {
26797         if(!this.canvasLoaded){
26798             this.beforeSelectFile(e);
26799             return;
26800         }
26801         
26802         var touches = e.browserEvent.touches;
26803         
26804         if(!touches){
26805             return;
26806         }
26807         
26808         if(touches.length == 1){
26809             this.onMouseDown(e);
26810             return;
26811         }
26812         
26813         if(touches.length != 2){
26814             return;
26815         }
26816         
26817         var coords = [];
26818         
26819         for(var i = 0, finger; finger = touches[i]; i++){
26820             coords.push(finger.pageX, finger.pageY);
26821         }
26822         
26823         var x = Math.pow(coords[0] - coords[2], 2);
26824         var y = Math.pow(coords[1] - coords[3], 2);
26825         
26826         this.startDistance = Math.sqrt(x + y);
26827         
26828         this.startScale = this.scale;
26829         
26830         this.pinching = true;
26831         this.dragable = false;
26832         
26833     },
26834     
26835     onTouchMove : function(e)
26836     {
26837         if(!this.pinching && !this.dragable){
26838             return;
26839         }
26840         
26841         var touches = e.browserEvent.touches;
26842         
26843         if(!touches){
26844             return;
26845         }
26846         
26847         if(this.dragable){
26848             this.onMouseMove(e);
26849             return;
26850         }
26851         
26852         var coords = [];
26853         
26854         for(var i = 0, finger; finger = touches[i]; i++){
26855             coords.push(finger.pageX, finger.pageY);
26856         }
26857         
26858         var x = Math.pow(coords[0] - coords[2], 2);
26859         var y = Math.pow(coords[1] - coords[3], 2);
26860         
26861         this.endDistance = Math.sqrt(x + y);
26862         
26863         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26864         
26865         if(!this.zoomable()){
26866             this.scale = this.startScale;
26867             return;
26868         }
26869         
26870         this.draw();
26871         
26872     },
26873     
26874     onTouchEnd : function(e)
26875     {
26876         this.pinching = false;
26877         this.dragable = false;
26878         
26879     },
26880     
26881     process : function(file, crop)
26882     {
26883         if(this.loadMask){
26884             this.maskEl.mask(this.loadingText);
26885         }
26886         
26887         this.xhr = new XMLHttpRequest();
26888         
26889         file.xhr = this.xhr;
26890
26891         this.xhr.open(this.method, this.url, true);
26892         
26893         var headers = {
26894             "Accept": "application/json",
26895             "Cache-Control": "no-cache",
26896             "X-Requested-With": "XMLHttpRequest"
26897         };
26898         
26899         for (var headerName in headers) {
26900             var headerValue = headers[headerName];
26901             if (headerValue) {
26902                 this.xhr.setRequestHeader(headerName, headerValue);
26903             }
26904         }
26905         
26906         var _this = this;
26907         
26908         this.xhr.onload = function()
26909         {
26910             _this.xhrOnLoad(_this.xhr);
26911         }
26912         
26913         this.xhr.onerror = function()
26914         {
26915             _this.xhrOnError(_this.xhr);
26916         }
26917         
26918         var formData = new FormData();
26919
26920         formData.append('returnHTML', 'NO');
26921         
26922         if(crop){
26923             formData.append('crop', crop);
26924         }
26925         
26926         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26927             formData.append(this.paramName, file, file.name);
26928         }
26929         
26930         if(typeof(file.filename) != 'undefined'){
26931             formData.append('filename', file.filename);
26932         }
26933         
26934         if(typeof(file.mimetype) != 'undefined'){
26935             formData.append('mimetype', file.mimetype);
26936         }
26937         
26938         if(this.fireEvent('arrange', this, formData) != false){
26939             this.xhr.send(formData);
26940         };
26941     },
26942     
26943     xhrOnLoad : function(xhr)
26944     {
26945         if(this.loadMask){
26946             this.maskEl.unmask();
26947         }
26948         
26949         if (xhr.readyState !== 4) {
26950             this.fireEvent('exception', this, xhr);
26951             return;
26952         }
26953
26954         var response = Roo.decode(xhr.responseText);
26955         
26956         if(!response.success){
26957             this.fireEvent('exception', this, xhr);
26958             return;
26959         }
26960         
26961         var response = Roo.decode(xhr.responseText);
26962         
26963         this.fireEvent('upload', this, response);
26964         
26965     },
26966     
26967     xhrOnError : function()
26968     {
26969         if(this.loadMask){
26970             this.maskEl.unmask();
26971         }
26972         
26973         Roo.log('xhr on error');
26974         
26975         var response = Roo.decode(xhr.responseText);
26976           
26977         Roo.log(response);
26978         
26979     },
26980     
26981     prepare : function(file)
26982     {   
26983         if(this.loadMask){
26984             this.maskEl.mask(this.loadingText);
26985         }
26986         
26987         this.file = false;
26988         this.exif = {};
26989         
26990         if(typeof(file) === 'string'){
26991             this.loadCanvas(file);
26992             return;
26993         }
26994         
26995         if(!file || !this.urlAPI){
26996             return;
26997         }
26998         
26999         this.file = file;
27000         this.cropType = file.type;
27001         
27002         var _this = this;
27003         
27004         if(this.fireEvent('prepare', this, this.file) != false){
27005             
27006             var reader = new FileReader();
27007             
27008             reader.onload = function (e) {
27009                 if (e.target.error) {
27010                     Roo.log(e.target.error);
27011                     return;
27012                 }
27013                 
27014                 var buffer = e.target.result,
27015                     dataView = new DataView(buffer),
27016                     offset = 2,
27017                     maxOffset = dataView.byteLength - 4,
27018                     markerBytes,
27019                     markerLength;
27020                 
27021                 if (dataView.getUint16(0) === 0xffd8) {
27022                     while (offset < maxOffset) {
27023                         markerBytes = dataView.getUint16(offset);
27024                         
27025                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27026                             markerLength = dataView.getUint16(offset + 2) + 2;
27027                             if (offset + markerLength > dataView.byteLength) {
27028                                 Roo.log('Invalid meta data: Invalid segment size.');
27029                                 break;
27030                             }
27031                             
27032                             if(markerBytes == 0xffe1){
27033                                 _this.parseExifData(
27034                                     dataView,
27035                                     offset,
27036                                     markerLength
27037                                 );
27038                             }
27039                             
27040                             offset += markerLength;
27041                             
27042                             continue;
27043                         }
27044                         
27045                         break;
27046                     }
27047                     
27048                 }
27049                 
27050                 var url = _this.urlAPI.createObjectURL(_this.file);
27051                 
27052                 _this.loadCanvas(url);
27053                 
27054                 return;
27055             }
27056             
27057             reader.readAsArrayBuffer(this.file);
27058             
27059         }
27060         
27061     },
27062     
27063     parseExifData : function(dataView, offset, length)
27064     {
27065         var tiffOffset = offset + 10,
27066             littleEndian,
27067             dirOffset;
27068     
27069         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27070             // No Exif data, might be XMP data instead
27071             return;
27072         }
27073         
27074         // Check for the ASCII code for "Exif" (0x45786966):
27075         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27076             // No Exif data, might be XMP data instead
27077             return;
27078         }
27079         if (tiffOffset + 8 > dataView.byteLength) {
27080             Roo.log('Invalid Exif data: Invalid segment size.');
27081             return;
27082         }
27083         // Check for the two null bytes:
27084         if (dataView.getUint16(offset + 8) !== 0x0000) {
27085             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27086             return;
27087         }
27088         // Check the byte alignment:
27089         switch (dataView.getUint16(tiffOffset)) {
27090         case 0x4949:
27091             littleEndian = true;
27092             break;
27093         case 0x4D4D:
27094             littleEndian = false;
27095             break;
27096         default:
27097             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27098             return;
27099         }
27100         // Check for the TIFF tag marker (0x002A):
27101         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27102             Roo.log('Invalid Exif data: Missing TIFF marker.');
27103             return;
27104         }
27105         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27106         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27107         
27108         this.parseExifTags(
27109             dataView,
27110             tiffOffset,
27111             tiffOffset + dirOffset,
27112             littleEndian
27113         );
27114     },
27115     
27116     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27117     {
27118         var tagsNumber,
27119             dirEndOffset,
27120             i;
27121         if (dirOffset + 6 > dataView.byteLength) {
27122             Roo.log('Invalid Exif data: Invalid directory offset.');
27123             return;
27124         }
27125         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27126         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27127         if (dirEndOffset + 4 > dataView.byteLength) {
27128             Roo.log('Invalid Exif data: Invalid directory size.');
27129             return;
27130         }
27131         for (i = 0; i < tagsNumber; i += 1) {
27132             this.parseExifTag(
27133                 dataView,
27134                 tiffOffset,
27135                 dirOffset + 2 + 12 * i, // tag offset
27136                 littleEndian
27137             );
27138         }
27139         // Return the offset to the next directory:
27140         return dataView.getUint32(dirEndOffset, littleEndian);
27141     },
27142     
27143     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
27144     {
27145         var tag = dataView.getUint16(offset, littleEndian);
27146         
27147         this.exif[tag] = this.getExifValue(
27148             dataView,
27149             tiffOffset,
27150             offset,
27151             dataView.getUint16(offset + 2, littleEndian), // tag type
27152             dataView.getUint32(offset + 4, littleEndian), // tag length
27153             littleEndian
27154         );
27155     },
27156     
27157     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27158     {
27159         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27160             tagSize,
27161             dataOffset,
27162             values,
27163             i,
27164             str,
27165             c;
27166     
27167         if (!tagType) {
27168             Roo.log('Invalid Exif data: Invalid tag type.');
27169             return;
27170         }
27171         
27172         tagSize = tagType.size * length;
27173         // Determine if the value is contained in the dataOffset bytes,
27174         // or if the value at the dataOffset is a pointer to the actual data:
27175         dataOffset = tagSize > 4 ?
27176                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27177         if (dataOffset + tagSize > dataView.byteLength) {
27178             Roo.log('Invalid Exif data: Invalid data offset.');
27179             return;
27180         }
27181         if (length === 1) {
27182             return tagType.getValue(dataView, dataOffset, littleEndian);
27183         }
27184         values = [];
27185         for (i = 0; i < length; i += 1) {
27186             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27187         }
27188         
27189         if (tagType.ascii) {
27190             str = '';
27191             // Concatenate the chars:
27192             for (i = 0; i < values.length; i += 1) {
27193                 c = values[i];
27194                 // Ignore the terminating NULL byte(s):
27195                 if (c === '\u0000') {
27196                     break;
27197                 }
27198                 str += c;
27199             }
27200             return str;
27201         }
27202         return values;
27203     }
27204     
27205 });
27206
27207 Roo.apply(Roo.bootstrap.UploadCropbox, {
27208     tags : {
27209         'Orientation': 0x0112
27210     },
27211     
27212     Orientation: {
27213             1: 0, //'top-left',
27214 //            2: 'top-right',
27215             3: 180, //'bottom-right',
27216 //            4: 'bottom-left',
27217 //            5: 'left-top',
27218             6: 90, //'right-top',
27219 //            7: 'right-bottom',
27220             8: 270 //'left-bottom'
27221     },
27222     
27223     exifTagTypes : {
27224         // byte, 8-bit unsigned int:
27225         1: {
27226             getValue: function (dataView, dataOffset) {
27227                 return dataView.getUint8(dataOffset);
27228             },
27229             size: 1
27230         },
27231         // ascii, 8-bit byte:
27232         2: {
27233             getValue: function (dataView, dataOffset) {
27234                 return String.fromCharCode(dataView.getUint8(dataOffset));
27235             },
27236             size: 1,
27237             ascii: true
27238         },
27239         // short, 16 bit int:
27240         3: {
27241             getValue: function (dataView, dataOffset, littleEndian) {
27242                 return dataView.getUint16(dataOffset, littleEndian);
27243             },
27244             size: 2
27245         },
27246         // long, 32 bit int:
27247         4: {
27248             getValue: function (dataView, dataOffset, littleEndian) {
27249                 return dataView.getUint32(dataOffset, littleEndian);
27250             },
27251             size: 4
27252         },
27253         // rational = two long values, first is numerator, second is denominator:
27254         5: {
27255             getValue: function (dataView, dataOffset, littleEndian) {
27256                 return dataView.getUint32(dataOffset, littleEndian) /
27257                     dataView.getUint32(dataOffset + 4, littleEndian);
27258             },
27259             size: 8
27260         },
27261         // slong, 32 bit signed int:
27262         9: {
27263             getValue: function (dataView, dataOffset, littleEndian) {
27264                 return dataView.getInt32(dataOffset, littleEndian);
27265             },
27266             size: 4
27267         },
27268         // srational, two slongs, first is numerator, second is denominator:
27269         10: {
27270             getValue: function (dataView, dataOffset, littleEndian) {
27271                 return dataView.getInt32(dataOffset, littleEndian) /
27272                     dataView.getInt32(dataOffset + 4, littleEndian);
27273             },
27274             size: 8
27275         }
27276     },
27277     
27278     footer : {
27279         STANDARD : [
27280             {
27281                 tag : 'div',
27282                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27283                 action : 'rotate-left',
27284                 cn : [
27285                     {
27286                         tag : 'button',
27287                         cls : 'btn btn-default',
27288                         html : '<i class="fa fa-undo"></i>'
27289                     }
27290                 ]
27291             },
27292             {
27293                 tag : 'div',
27294                 cls : 'btn-group roo-upload-cropbox-picture',
27295                 action : 'picture',
27296                 cn : [
27297                     {
27298                         tag : 'button',
27299                         cls : 'btn btn-default',
27300                         html : '<i class="fa fa-picture-o"></i>'
27301                     }
27302                 ]
27303             },
27304             {
27305                 tag : 'div',
27306                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27307                 action : 'rotate-right',
27308                 cn : [
27309                     {
27310                         tag : 'button',
27311                         cls : 'btn btn-default',
27312                         html : '<i class="fa fa-repeat"></i>'
27313                     }
27314                 ]
27315             }
27316         ],
27317         DOCUMENT : [
27318             {
27319                 tag : 'div',
27320                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27321                 action : 'rotate-left',
27322                 cn : [
27323                     {
27324                         tag : 'button',
27325                         cls : 'btn btn-default',
27326                         html : '<i class="fa fa-undo"></i>'
27327                     }
27328                 ]
27329             },
27330             {
27331                 tag : 'div',
27332                 cls : 'btn-group roo-upload-cropbox-download',
27333                 action : 'download',
27334                 cn : [
27335                     {
27336                         tag : 'button',
27337                         cls : 'btn btn-default',
27338                         html : '<i class="fa fa-download"></i>'
27339                     }
27340                 ]
27341             },
27342             {
27343                 tag : 'div',
27344                 cls : 'btn-group roo-upload-cropbox-crop',
27345                 action : 'crop',
27346                 cn : [
27347                     {
27348                         tag : 'button',
27349                         cls : 'btn btn-default',
27350                         html : '<i class="fa fa-crop"></i>'
27351                     }
27352                 ]
27353             },
27354             {
27355                 tag : 'div',
27356                 cls : 'btn-group roo-upload-cropbox-trash',
27357                 action : 'trash',
27358                 cn : [
27359                     {
27360                         tag : 'button',
27361                         cls : 'btn btn-default',
27362                         html : '<i class="fa fa-trash"></i>'
27363                     }
27364                 ]
27365             },
27366             {
27367                 tag : 'div',
27368                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27369                 action : 'rotate-right',
27370                 cn : [
27371                     {
27372                         tag : 'button',
27373                         cls : 'btn btn-default',
27374                         html : '<i class="fa fa-repeat"></i>'
27375                     }
27376                 ]
27377             }
27378         ],
27379         ROTATOR : [
27380             {
27381                 tag : 'div',
27382                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27383                 action : 'rotate-left',
27384                 cn : [
27385                     {
27386                         tag : 'button',
27387                         cls : 'btn btn-default',
27388                         html : '<i class="fa fa-undo"></i>'
27389                     }
27390                 ]
27391             },
27392             {
27393                 tag : 'div',
27394                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27395                 action : 'rotate-right',
27396                 cn : [
27397                     {
27398                         tag : 'button',
27399                         cls : 'btn btn-default',
27400                         html : '<i class="fa fa-repeat"></i>'
27401                     }
27402                 ]
27403             }
27404         ]
27405     }
27406 });
27407
27408 /*
27409 * Licence: LGPL
27410 */
27411
27412 /**
27413  * @class Roo.bootstrap.DocumentManager
27414  * @extends Roo.bootstrap.Component
27415  * Bootstrap DocumentManager class
27416  * @cfg {String} paramName default 'imageUpload'
27417  * @cfg {String} toolTipName default 'filename'
27418  * @cfg {String} method default POST
27419  * @cfg {String} url action url
27420  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27421  * @cfg {Boolean} multiple multiple upload default true
27422  * @cfg {Number} thumbSize default 300
27423  * @cfg {String} fieldLabel
27424  * @cfg {Number} labelWidth default 4
27425  * @cfg {String} labelAlign (left|top) default left
27426  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27427  * 
27428  * @constructor
27429  * Create a new DocumentManager
27430  * @param {Object} config The config object
27431  */
27432
27433 Roo.bootstrap.DocumentManager = function(config){
27434     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27435     
27436     this.files = [];
27437     this.delegates = [];
27438     
27439     this.addEvents({
27440         /**
27441          * @event initial
27442          * Fire when initial the DocumentManager
27443          * @param {Roo.bootstrap.DocumentManager} this
27444          */
27445         "initial" : true,
27446         /**
27447          * @event inspect
27448          * inspect selected file
27449          * @param {Roo.bootstrap.DocumentManager} this
27450          * @param {File} file
27451          */
27452         "inspect" : true,
27453         /**
27454          * @event exception
27455          * Fire when xhr load exception
27456          * @param {Roo.bootstrap.DocumentManager} this
27457          * @param {XMLHttpRequest} xhr
27458          */
27459         "exception" : true,
27460         /**
27461          * @event afterupload
27462          * Fire when xhr load exception
27463          * @param {Roo.bootstrap.DocumentManager} this
27464          * @param {XMLHttpRequest} xhr
27465          */
27466         "afterupload" : true,
27467         /**
27468          * @event prepare
27469          * prepare the form data
27470          * @param {Roo.bootstrap.DocumentManager} this
27471          * @param {Object} formData
27472          */
27473         "prepare" : true,
27474         /**
27475          * @event remove
27476          * Fire when remove the file
27477          * @param {Roo.bootstrap.DocumentManager} this
27478          * @param {Object} file
27479          */
27480         "remove" : true,
27481         /**
27482          * @event refresh
27483          * Fire after refresh the file
27484          * @param {Roo.bootstrap.DocumentManager} this
27485          */
27486         "refresh" : true,
27487         /**
27488          * @event click
27489          * Fire after click the image
27490          * @param {Roo.bootstrap.DocumentManager} this
27491          * @param {Object} file
27492          */
27493         "click" : true,
27494         /**
27495          * @event edit
27496          * Fire when upload a image and editable set to true
27497          * @param {Roo.bootstrap.DocumentManager} this
27498          * @param {Object} file
27499          */
27500         "edit" : true,
27501         /**
27502          * @event beforeselectfile
27503          * Fire before select file
27504          * @param {Roo.bootstrap.DocumentManager} this
27505          */
27506         "beforeselectfile" : true,
27507         /**
27508          * @event process
27509          * Fire before process file
27510          * @param {Roo.bootstrap.DocumentManager} this
27511          * @param {Object} file
27512          */
27513         "process" : true
27514         
27515     });
27516 };
27517
27518 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
27519     
27520     boxes : 0,
27521     inputName : '',
27522     thumbSize : 300,
27523     multiple : true,
27524     files : false,
27525     method : 'POST',
27526     url : '',
27527     paramName : 'imageUpload',
27528     toolTipName : 'filename',
27529     fieldLabel : '',
27530     labelWidth : 4,
27531     labelAlign : 'left',
27532     editable : true,
27533     delegates : false,
27534     xhr : false, 
27535     
27536     getAutoCreate : function()
27537     {   
27538         var managerWidget = {
27539             tag : 'div',
27540             cls : 'roo-document-manager',
27541             cn : [
27542                 {
27543                     tag : 'input',
27544                     cls : 'roo-document-manager-selector',
27545                     type : 'file'
27546                 },
27547                 {
27548                     tag : 'div',
27549                     cls : 'roo-document-manager-uploader',
27550                     cn : [
27551                         {
27552                             tag : 'div',
27553                             cls : 'roo-document-manager-upload-btn',
27554                             html : '<i class="fa fa-plus"></i>'
27555                         }
27556                     ]
27557                     
27558                 }
27559             ]
27560         };
27561         
27562         var content = [
27563             {
27564                 tag : 'div',
27565                 cls : 'column col-md-12',
27566                 cn : managerWidget
27567             }
27568         ];
27569         
27570         if(this.fieldLabel.length){
27571             
27572             content = [
27573                 {
27574                     tag : 'div',
27575                     cls : 'column col-md-12',
27576                     html : this.fieldLabel
27577                 },
27578                 {
27579                     tag : 'div',
27580                     cls : 'column col-md-12',
27581                     cn : managerWidget
27582                 }
27583             ];
27584
27585             if(this.labelAlign == 'left'){
27586                 content = [
27587                     {
27588                         tag : 'div',
27589                         cls : 'column col-md-' + this.labelWidth,
27590                         html : this.fieldLabel
27591                     },
27592                     {
27593                         tag : 'div',
27594                         cls : 'column col-md-' + (12 - this.labelWidth),
27595                         cn : managerWidget
27596                     }
27597                 ];
27598                 
27599             }
27600         }
27601         
27602         var cfg = {
27603             tag : 'div',
27604             cls : 'row clearfix',
27605             cn : content
27606         };
27607         
27608         return cfg;
27609         
27610     },
27611     
27612     initEvents : function()
27613     {
27614         this.managerEl = this.el.select('.roo-document-manager', true).first();
27615         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27616         
27617         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27618         this.selectorEl.hide();
27619         
27620         if(this.multiple){
27621             this.selectorEl.attr('multiple', 'multiple');
27622         }
27623         
27624         this.selectorEl.on('change', this.onFileSelected, this);
27625         
27626         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27627         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27628         
27629         this.uploader.on('click', this.onUploaderClick, this);
27630         
27631         this.renderProgressDialog();
27632         
27633         var _this = this;
27634         
27635         window.addEventListener("resize", function() { _this.refresh(); } );
27636         
27637         this.fireEvent('initial', this);
27638     },
27639     
27640     renderProgressDialog : function()
27641     {
27642         var _this = this;
27643         
27644         this.progressDialog = new Roo.bootstrap.Modal({
27645             cls : 'roo-document-manager-progress-dialog',
27646             allow_close : false,
27647             title : '',
27648             buttons : [
27649                 {
27650                     name  :'cancel',
27651                     weight : 'danger',
27652                     html : 'Cancel'
27653                 }
27654             ], 
27655             listeners : { 
27656                 btnclick : function() {
27657                     _this.uploadCancel();
27658                     this.hide();
27659                 }
27660             }
27661         });
27662          
27663         this.progressDialog.render(Roo.get(document.body));
27664          
27665         this.progress = new Roo.bootstrap.Progress({
27666             cls : 'roo-document-manager-progress',
27667             active : true,
27668             striped : true
27669         });
27670         
27671         this.progress.render(this.progressDialog.getChildContainer());
27672         
27673         this.progressBar = new Roo.bootstrap.ProgressBar({
27674             cls : 'roo-document-manager-progress-bar',
27675             aria_valuenow : 0,
27676             aria_valuemin : 0,
27677             aria_valuemax : 12,
27678             panel : 'success'
27679         });
27680         
27681         this.progressBar.render(this.progress.getChildContainer());
27682     },
27683     
27684     onUploaderClick : function(e)
27685     {
27686         e.preventDefault();
27687      
27688         if(this.fireEvent('beforeselectfile', this) != false){
27689             this.selectorEl.dom.click();
27690         }
27691         
27692     },
27693     
27694     onFileSelected : function(e)
27695     {
27696         e.preventDefault();
27697         
27698         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27699             return;
27700         }
27701         
27702         Roo.each(this.selectorEl.dom.files, function(file){
27703             if(this.fireEvent('inspect', this, file) != false){
27704                 this.files.push(file);
27705             }
27706         }, this);
27707         
27708         this.queue();
27709         
27710     },
27711     
27712     queue : function()
27713     {
27714         this.selectorEl.dom.value = '';
27715         
27716         if(!this.files.length){
27717             return;
27718         }
27719         
27720         if(this.boxes > 0 && this.files.length > this.boxes){
27721             this.files = this.files.slice(0, this.boxes);
27722         }
27723         
27724         this.uploader.show();
27725         
27726         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27727             this.uploader.hide();
27728         }
27729         
27730         var _this = this;
27731         
27732         var files = [];
27733         
27734         var docs = [];
27735         
27736         Roo.each(this.files, function(file){
27737             
27738             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27739                 var f = this.renderPreview(file);
27740                 files.push(f);
27741                 return;
27742             }
27743             
27744             if(file.type.indexOf('image') != -1){
27745                 this.delegates.push(
27746                     (function(){
27747                         _this.process(file);
27748                     }).createDelegate(this)
27749                 );
27750         
27751                 return;
27752             }
27753             
27754             docs.push(
27755                 (function(){
27756                     _this.process(file);
27757                 }).createDelegate(this)
27758             );
27759             
27760         }, this);
27761         
27762         this.files = files;
27763         
27764         this.delegates = this.delegates.concat(docs);
27765         
27766         if(!this.delegates.length){
27767             this.refresh();
27768             return;
27769         }
27770         
27771         this.progressBar.aria_valuemax = this.delegates.length;
27772         
27773         this.arrange();
27774         
27775         return;
27776     },
27777     
27778     arrange : function()
27779     {
27780         if(!this.delegates.length){
27781             this.progressDialog.hide();
27782             this.refresh();
27783             return;
27784         }
27785         
27786         var delegate = this.delegates.shift();
27787         
27788         this.progressDialog.show();
27789         
27790         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27791         
27792         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27793         
27794         delegate();
27795     },
27796     
27797     refresh : function()
27798     {
27799         this.uploader.show();
27800         
27801         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27802             this.uploader.hide();
27803         }
27804         
27805         Roo.isTouch ? this.closable(false) : this.closable(true);
27806         
27807         this.fireEvent('refresh', this);
27808     },
27809     
27810     onRemove : function(e, el, o)
27811     {
27812         e.preventDefault();
27813         
27814         this.fireEvent('remove', this, o);
27815         
27816     },
27817     
27818     remove : function(o)
27819     {
27820         var files = [];
27821         
27822         Roo.each(this.files, function(file){
27823             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27824                 files.push(file);
27825                 return;
27826             }
27827
27828             o.target.remove();
27829
27830         }, this);
27831         
27832         this.files = files;
27833         
27834         this.refresh();
27835     },
27836     
27837     clear : function()
27838     {
27839         Roo.each(this.files, function(file){
27840             if(!file.target){
27841                 return;
27842             }
27843             
27844             file.target.remove();
27845
27846         }, this);
27847         
27848         this.files = [];
27849         
27850         this.refresh();
27851     },
27852     
27853     onClick : function(e, el, o)
27854     {
27855         e.preventDefault();
27856         
27857         this.fireEvent('click', this, o);
27858         
27859     },
27860     
27861     closable : function(closable)
27862     {
27863         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27864             
27865             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27866             
27867             if(closable){
27868                 el.show();
27869                 return;
27870             }
27871             
27872             el.hide();
27873             
27874         }, this);
27875     },
27876     
27877     xhrOnLoad : function(xhr)
27878     {
27879         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27880             el.remove();
27881         }, this);
27882         
27883         if (xhr.readyState !== 4) {
27884             this.arrange();
27885             this.fireEvent('exception', this, xhr);
27886             return;
27887         }
27888
27889         var response = Roo.decode(xhr.responseText);
27890         
27891         if(!response.success){
27892             this.arrange();
27893             this.fireEvent('exception', this, xhr);
27894             return;
27895         }
27896         
27897         var file = this.renderPreview(response.data);
27898         
27899         this.files.push(file);
27900         
27901         this.arrange();
27902         
27903         this.fireEvent('afterupload', this, xhr);
27904         
27905     },
27906     
27907     xhrOnError : function(xhr)
27908     {
27909         Roo.log('xhr on error');
27910         
27911         var response = Roo.decode(xhr.responseText);
27912           
27913         Roo.log(response);
27914         
27915         this.arrange();
27916     },
27917     
27918     process : function(file)
27919     {
27920         if(this.fireEvent('process', this, file) !== false){
27921             if(this.editable && file.type.indexOf('image') != -1){
27922                 this.fireEvent('edit', this, file);
27923                 return;
27924             }
27925
27926             this.uploadStart(file, false);
27927
27928             return;
27929         }
27930         
27931     },
27932     
27933     uploadStart : function(file, crop)
27934     {
27935         this.xhr = new XMLHttpRequest();
27936         
27937         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27938             this.arrange();
27939             return;
27940         }
27941         
27942         file.xhr = this.xhr;
27943             
27944         this.managerEl.createChild({
27945             tag : 'div',
27946             cls : 'roo-document-manager-loading',
27947             cn : [
27948                 {
27949                     tag : 'div',
27950                     tooltip : file.name,
27951                     cls : 'roo-document-manager-thumb',
27952                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27953                 }
27954             ]
27955
27956         });
27957
27958         this.xhr.open(this.method, this.url, true);
27959         
27960         var headers = {
27961             "Accept": "application/json",
27962             "Cache-Control": "no-cache",
27963             "X-Requested-With": "XMLHttpRequest"
27964         };
27965         
27966         for (var headerName in headers) {
27967             var headerValue = headers[headerName];
27968             if (headerValue) {
27969                 this.xhr.setRequestHeader(headerName, headerValue);
27970             }
27971         }
27972         
27973         var _this = this;
27974         
27975         this.xhr.onload = function()
27976         {
27977             _this.xhrOnLoad(_this.xhr);
27978         }
27979         
27980         this.xhr.onerror = function()
27981         {
27982             _this.xhrOnError(_this.xhr);
27983         }
27984         
27985         var formData = new FormData();
27986
27987         formData.append('returnHTML', 'NO');
27988         
27989         if(crop){
27990             formData.append('crop', crop);
27991         }
27992         
27993         formData.append(this.paramName, file, file.name);
27994         
27995         var options = {
27996             file : file, 
27997             manually : false
27998         };
27999         
28000         if(this.fireEvent('prepare', this, formData, options) != false){
28001             
28002             if(options.manually){
28003                 return;
28004             }
28005             
28006             this.xhr.send(formData);
28007             return;
28008         };
28009         
28010         this.uploadCancel();
28011     },
28012     
28013     uploadCancel : function()
28014     {
28015         if (this.xhr) {
28016             this.xhr.abort();
28017         }
28018         
28019         this.delegates = [];
28020         
28021         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28022             el.remove();
28023         }, this);
28024         
28025         this.arrange();
28026     },
28027     
28028     renderPreview : function(file)
28029     {
28030         if(typeof(file.target) != 'undefined' && file.target){
28031             return file;
28032         }
28033         
28034         var previewEl = this.managerEl.createChild({
28035             tag : 'div',
28036             cls : 'roo-document-manager-preview',
28037             cn : [
28038                 {
28039                     tag : 'div',
28040                     tooltip : file[this.toolTipName],
28041                     cls : 'roo-document-manager-thumb',
28042                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
28043                 },
28044                 {
28045                     tag : 'button',
28046                     cls : 'close',
28047                     html : '<i class="fa fa-times-circle"></i>'
28048                 }
28049             ]
28050         });
28051
28052         var close = previewEl.select('button.close', true).first();
28053
28054         close.on('click', this.onRemove, this, file);
28055
28056         file.target = previewEl;
28057
28058         var image = previewEl.select('img', true).first();
28059         
28060         var _this = this;
28061         
28062         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28063         
28064         image.on('click', this.onClick, this, file);
28065         
28066         return file;
28067         
28068     },
28069     
28070     onPreviewLoad : function(file, image)
28071     {
28072         if(typeof(file.target) == 'undefined' || !file.target){
28073             return;
28074         }
28075         
28076         var width = image.dom.naturalWidth || image.dom.width;
28077         var height = image.dom.naturalHeight || image.dom.height;
28078         
28079         if(width > height){
28080             file.target.addClass('wide');
28081             return;
28082         }
28083         
28084         file.target.addClass('tall');
28085         return;
28086         
28087     },
28088     
28089     uploadFromSource : function(file, crop)
28090     {
28091         this.xhr = new XMLHttpRequest();
28092         
28093         this.managerEl.createChild({
28094             tag : 'div',
28095             cls : 'roo-document-manager-loading',
28096             cn : [
28097                 {
28098                     tag : 'div',
28099                     tooltip : file.name,
28100                     cls : 'roo-document-manager-thumb',
28101                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28102                 }
28103             ]
28104
28105         });
28106
28107         this.xhr.open(this.method, this.url, true);
28108         
28109         var headers = {
28110             "Accept": "application/json",
28111             "Cache-Control": "no-cache",
28112             "X-Requested-With": "XMLHttpRequest"
28113         };
28114         
28115         for (var headerName in headers) {
28116             var headerValue = headers[headerName];
28117             if (headerValue) {
28118                 this.xhr.setRequestHeader(headerName, headerValue);
28119             }
28120         }
28121         
28122         var _this = this;
28123         
28124         this.xhr.onload = function()
28125         {
28126             _this.xhrOnLoad(_this.xhr);
28127         }
28128         
28129         this.xhr.onerror = function()
28130         {
28131             _this.xhrOnError(_this.xhr);
28132         }
28133         
28134         var formData = new FormData();
28135
28136         formData.append('returnHTML', 'NO');
28137         
28138         formData.append('crop', crop);
28139         
28140         if(typeof(file.filename) != 'undefined'){
28141             formData.append('filename', file.filename);
28142         }
28143         
28144         if(typeof(file.mimetype) != 'undefined'){
28145             formData.append('mimetype', file.mimetype);
28146         }
28147         
28148         if(this.fireEvent('prepare', this, formData) != false){
28149             this.xhr.send(formData);
28150         };
28151     }
28152 });
28153
28154 /*
28155 * Licence: LGPL
28156 */
28157
28158 /**
28159  * @class Roo.bootstrap.DocumentViewer
28160  * @extends Roo.bootstrap.Component
28161  * Bootstrap DocumentViewer class
28162  * @cfg {Boolean} showDownload (true|false) show download button (default true)
28163  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28164  * 
28165  * @constructor
28166  * Create a new DocumentViewer
28167  * @param {Object} config The config object
28168  */
28169
28170 Roo.bootstrap.DocumentViewer = function(config){
28171     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28172     
28173     this.addEvents({
28174         /**
28175          * @event initial
28176          * Fire after initEvent
28177          * @param {Roo.bootstrap.DocumentViewer} this
28178          */
28179         "initial" : true,
28180         /**
28181          * @event click
28182          * Fire after click
28183          * @param {Roo.bootstrap.DocumentViewer} this
28184          */
28185         "click" : true,
28186         /**
28187          * @event download
28188          * Fire after download button
28189          * @param {Roo.bootstrap.DocumentViewer} this
28190          */
28191         "download" : true,
28192         /**
28193          * @event trash
28194          * Fire after trash button
28195          * @param {Roo.bootstrap.DocumentViewer} this
28196          */
28197         "trash" : true
28198         
28199     });
28200 };
28201
28202 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
28203     
28204     showDownload : true,
28205     
28206     showTrash : true,
28207     
28208     getAutoCreate : function()
28209     {
28210         var cfg = {
28211             tag : 'div',
28212             cls : 'roo-document-viewer',
28213             cn : [
28214                 {
28215                     tag : 'div',
28216                     cls : 'roo-document-viewer-body',
28217                     cn : [
28218                         {
28219                             tag : 'div',
28220                             cls : 'roo-document-viewer-thumb',
28221                             cn : [
28222                                 {
28223                                     tag : 'img',
28224                                     cls : 'roo-document-viewer-image'
28225                                 }
28226                             ]
28227                         }
28228                     ]
28229                 },
28230                 {
28231                     tag : 'div',
28232                     cls : 'roo-document-viewer-footer',
28233                     cn : {
28234                         tag : 'div',
28235                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
28236                         cn : [
28237                             {
28238                                 tag : 'div',
28239                                 cls : 'btn-group roo-document-viewer-download',
28240                                 cn : [
28241                                     {
28242                                         tag : 'button',
28243                                         cls : 'btn btn-default',
28244                                         html : '<i class="fa fa-download"></i>'
28245                                     }
28246                                 ]
28247                             },
28248                             {
28249                                 tag : 'div',
28250                                 cls : 'btn-group roo-document-viewer-trash',
28251                                 cn : [
28252                                     {
28253                                         tag : 'button',
28254                                         cls : 'btn btn-default',
28255                                         html : '<i class="fa fa-trash"></i>'
28256                                     }
28257                                 ]
28258                             }
28259                         ]
28260                     }
28261                 }
28262             ]
28263         };
28264         
28265         return cfg;
28266     },
28267     
28268     initEvents : function()
28269     {
28270         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
28271         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
28272         
28273         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
28274         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
28275         
28276         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
28277         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
28278         
28279         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
28280         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
28281         
28282         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
28283         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
28284         
28285         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
28286         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
28287         
28288         this.bodyEl.on('click', this.onClick, this);
28289         this.downloadBtn.on('click', this.onDownload, this);
28290         this.trashBtn.on('click', this.onTrash, this);
28291         
28292         this.downloadBtn.hide();
28293         this.trashBtn.hide();
28294         
28295         if(this.showDownload){
28296             this.downloadBtn.show();
28297         }
28298         
28299         if(this.showTrash){
28300             this.trashBtn.show();
28301         }
28302         
28303         if(!this.showDownload && !this.showTrash) {
28304             this.footerEl.hide();
28305         }
28306         
28307     },
28308     
28309     initial : function()
28310     {
28311         this.fireEvent('initial', this);
28312         
28313     },
28314     
28315     onClick : function(e)
28316     {
28317         e.preventDefault();
28318         
28319         this.fireEvent('click', this);
28320     },
28321     
28322     onDownload : function(e)
28323     {
28324         e.preventDefault();
28325         
28326         this.fireEvent('download', this);
28327     },
28328     
28329     onTrash : function(e)
28330     {
28331         e.preventDefault();
28332         
28333         this.fireEvent('trash', this);
28334     }
28335     
28336 });
28337 /*
28338  * - LGPL
28339  *
28340  * nav progress bar
28341  * 
28342  */
28343
28344 /**
28345  * @class Roo.bootstrap.NavProgressBar
28346  * @extends Roo.bootstrap.Component
28347  * Bootstrap NavProgressBar class
28348  * 
28349  * @constructor
28350  * Create a new nav progress bar
28351  * @param {Object} config The config object
28352  */
28353
28354 Roo.bootstrap.NavProgressBar = function(config){
28355     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
28356
28357     this.bullets = this.bullets || [];
28358    
28359 //    Roo.bootstrap.NavProgressBar.register(this);
28360      this.addEvents({
28361         /**
28362              * @event changed
28363              * Fires when the active item changes
28364              * @param {Roo.bootstrap.NavProgressBar} this
28365              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28366              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
28367          */
28368         'changed': true
28369      });
28370     
28371 };
28372
28373 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
28374     
28375     bullets : [],
28376     barItems : [],
28377     
28378     getAutoCreate : function()
28379     {
28380         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28381         
28382         cfg = {
28383             tag : 'div',
28384             cls : 'roo-navigation-bar-group',
28385             cn : [
28386                 {
28387                     tag : 'div',
28388                     cls : 'roo-navigation-top-bar'
28389                 },
28390                 {
28391                     tag : 'div',
28392                     cls : 'roo-navigation-bullets-bar',
28393                     cn : [
28394                         {
28395                             tag : 'ul',
28396                             cls : 'roo-navigation-bar'
28397                         }
28398                     ]
28399                 },
28400                 
28401                 {
28402                     tag : 'div',
28403                     cls : 'roo-navigation-bottom-bar'
28404                 }
28405             ]
28406             
28407         };
28408         
28409         return cfg;
28410         
28411     },
28412     
28413     initEvents: function() 
28414     {
28415         
28416     },
28417     
28418     onRender : function(ct, position) 
28419     {
28420         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28421         
28422         if(this.bullets.length){
28423             Roo.each(this.bullets, function(b){
28424                this.addItem(b);
28425             }, this);
28426         }
28427         
28428         this.format();
28429         
28430     },
28431     
28432     addItem : function(cfg)
28433     {
28434         var item = new Roo.bootstrap.NavProgressItem(cfg);
28435         
28436         item.parentId = this.id;
28437         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28438         
28439         if(cfg.html){
28440             var top = new Roo.bootstrap.Element({
28441                 tag : 'div',
28442                 cls : 'roo-navigation-bar-text'
28443             });
28444             
28445             var bottom = new Roo.bootstrap.Element({
28446                 tag : 'div',
28447                 cls : 'roo-navigation-bar-text'
28448             });
28449             
28450             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28451             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28452             
28453             var topText = new Roo.bootstrap.Element({
28454                 tag : 'span',
28455                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28456             });
28457             
28458             var bottomText = new Roo.bootstrap.Element({
28459                 tag : 'span',
28460                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28461             });
28462             
28463             topText.onRender(top.el, null);
28464             bottomText.onRender(bottom.el, null);
28465             
28466             item.topEl = top;
28467             item.bottomEl = bottom;
28468         }
28469         
28470         this.barItems.push(item);
28471         
28472         return item;
28473     },
28474     
28475     getActive : function()
28476     {
28477         var active = false;
28478         
28479         Roo.each(this.barItems, function(v){
28480             
28481             if (!v.isActive()) {
28482                 return;
28483             }
28484             
28485             active = v;
28486             return false;
28487             
28488         });
28489         
28490         return active;
28491     },
28492     
28493     setActiveItem : function(item)
28494     {
28495         var prev = false;
28496         
28497         Roo.each(this.barItems, function(v){
28498             if (v.rid == item.rid) {
28499                 return ;
28500             }
28501             
28502             if (v.isActive()) {
28503                 v.setActive(false);
28504                 prev = v;
28505             }
28506         });
28507
28508         item.setActive(true);
28509         
28510         this.fireEvent('changed', this, item, prev);
28511     },
28512     
28513     getBarItem: function(rid)
28514     {
28515         var ret = false;
28516         
28517         Roo.each(this.barItems, function(e) {
28518             if (e.rid != rid) {
28519                 return;
28520             }
28521             
28522             ret =  e;
28523             return false;
28524         });
28525         
28526         return ret;
28527     },
28528     
28529     indexOfItem : function(item)
28530     {
28531         var index = false;
28532         
28533         Roo.each(this.barItems, function(v, i){
28534             
28535             if (v.rid != item.rid) {
28536                 return;
28537             }
28538             
28539             index = i;
28540             return false
28541         });
28542         
28543         return index;
28544     },
28545     
28546     setActiveNext : function()
28547     {
28548         var i = this.indexOfItem(this.getActive());
28549         
28550         if (i > this.barItems.length) {
28551             return;
28552         }
28553         
28554         this.setActiveItem(this.barItems[i+1]);
28555     },
28556     
28557     setActivePrev : function()
28558     {
28559         var i = this.indexOfItem(this.getActive());
28560         
28561         if (i  < 1) {
28562             return;
28563         }
28564         
28565         this.setActiveItem(this.barItems[i-1]);
28566     },
28567     
28568     format : function()
28569     {
28570         if(!this.barItems.length){
28571             return;
28572         }
28573      
28574         var width = 100 / this.barItems.length;
28575         
28576         Roo.each(this.barItems, function(i){
28577             i.el.setStyle('width', width + '%');
28578             i.topEl.el.setStyle('width', width + '%');
28579             i.bottomEl.el.setStyle('width', width + '%');
28580         }, this);
28581         
28582     }
28583     
28584 });
28585 /*
28586  * - LGPL
28587  *
28588  * Nav Progress Item
28589  * 
28590  */
28591
28592 /**
28593  * @class Roo.bootstrap.NavProgressItem
28594  * @extends Roo.bootstrap.Component
28595  * Bootstrap NavProgressItem class
28596  * @cfg {String} rid the reference id
28597  * @cfg {Boolean} active (true|false) Is item active default false
28598  * @cfg {Boolean} disabled (true|false) Is item active default false
28599  * @cfg {String} html
28600  * @cfg {String} position (top|bottom) text position default bottom
28601  * @cfg {String} icon show icon instead of number
28602  * 
28603  * @constructor
28604  * Create a new NavProgressItem
28605  * @param {Object} config The config object
28606  */
28607 Roo.bootstrap.NavProgressItem = function(config){
28608     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28609     this.addEvents({
28610         // raw events
28611         /**
28612          * @event click
28613          * The raw click event for the entire grid.
28614          * @param {Roo.bootstrap.NavProgressItem} this
28615          * @param {Roo.EventObject} e
28616          */
28617         "click" : true
28618     });
28619    
28620 };
28621
28622 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
28623     
28624     rid : '',
28625     active : false,
28626     disabled : false,
28627     html : '',
28628     position : 'bottom',
28629     icon : false,
28630     
28631     getAutoCreate : function()
28632     {
28633         var iconCls = 'roo-navigation-bar-item-icon';
28634         
28635         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28636         
28637         var cfg = {
28638             tag: 'li',
28639             cls: 'roo-navigation-bar-item',
28640             cn : [
28641                 {
28642                     tag : 'i',
28643                     cls : iconCls
28644                 }
28645             ]
28646         };
28647         
28648         if(this.active){
28649             cfg.cls += ' active';
28650         }
28651         if(this.disabled){
28652             cfg.cls += ' disabled';
28653         }
28654         
28655         return cfg;
28656     },
28657     
28658     disable : function()
28659     {
28660         this.setDisabled(true);
28661     },
28662     
28663     enable : function()
28664     {
28665         this.setDisabled(false);
28666     },
28667     
28668     initEvents: function() 
28669     {
28670         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28671         
28672         this.iconEl.on('click', this.onClick, this);
28673     },
28674     
28675     onClick : function(e)
28676     {
28677         e.preventDefault();
28678         
28679         if(this.disabled){
28680             return;
28681         }
28682         
28683         if(this.fireEvent('click', this, e) === false){
28684             return;
28685         };
28686         
28687         this.parent().setActiveItem(this);
28688     },
28689     
28690     isActive: function () 
28691     {
28692         return this.active;
28693     },
28694     
28695     setActive : function(state)
28696     {
28697         if(this.active == state){
28698             return;
28699         }
28700         
28701         this.active = state;
28702         
28703         if (state) {
28704             this.el.addClass('active');
28705             return;
28706         }
28707         
28708         this.el.removeClass('active');
28709         
28710         return;
28711     },
28712     
28713     setDisabled : function(state)
28714     {
28715         if(this.disabled == state){
28716             return;
28717         }
28718         
28719         this.disabled = state;
28720         
28721         if (state) {
28722             this.el.addClass('disabled');
28723             return;
28724         }
28725         
28726         this.el.removeClass('disabled');
28727     },
28728     
28729     tooltipEl : function()
28730     {
28731         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28732     }
28733 });
28734  
28735
28736  /*
28737  * - LGPL
28738  *
28739  * FieldLabel
28740  * 
28741  */
28742
28743 /**
28744  * @class Roo.bootstrap.FieldLabel
28745  * @extends Roo.bootstrap.Component
28746  * Bootstrap FieldLabel class
28747  * @cfg {String} html contents of the element
28748  * @cfg {String} tag tag of the element default label
28749  * @cfg {String} cls class of the element
28750  * @cfg {String} target label target 
28751  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28752  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28753  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28754  * @cfg {String} iconTooltip default "This field is required"
28755  * 
28756  * @constructor
28757  * Create a new FieldLabel
28758  * @param {Object} config The config object
28759  */
28760
28761 Roo.bootstrap.FieldLabel = function(config){
28762     Roo.bootstrap.Element.superclass.constructor.call(this, config);
28763     
28764     this.addEvents({
28765             /**
28766              * @event invalid
28767              * Fires after the field has been marked as invalid.
28768              * @param {Roo.form.FieldLabel} this
28769              * @param {String} msg The validation message
28770              */
28771             invalid : true,
28772             /**
28773              * @event valid
28774              * Fires after the field has been validated with no errors.
28775              * @param {Roo.form.FieldLabel} this
28776              */
28777             valid : true
28778         });
28779 };
28780
28781 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
28782     
28783     tag: 'label',
28784     cls: '',
28785     html: '',
28786     target: '',
28787     allowBlank : true,
28788     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28789     validClass : 'text-success fa fa-lg fa-check',
28790     iconTooltip : 'This field is required',
28791     
28792     getAutoCreate : function(){
28793         
28794         var cfg = {
28795             tag : this.tag,
28796             cls : 'roo-bootstrap-field-label ' + this.cls,
28797             for : this.target,
28798             cn : [
28799                 {
28800                     tag : 'i',
28801                     cls : '',
28802                     tooltip : this.iconTooltip
28803                 },
28804                 {
28805                     tag : 'span',
28806                     html : this.html
28807                 }
28808             ] 
28809         };
28810         
28811         return cfg;
28812     },
28813     
28814     initEvents: function() 
28815     {
28816         Roo.bootstrap.Element.superclass.initEvents.call(this);
28817         
28818         this.iconEl = this.el.select('i', true).first();
28819         
28820         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28821         
28822         Roo.bootstrap.FieldLabel.register(this);
28823     },
28824     
28825     /**
28826      * Mark this field as valid
28827      */
28828     markValid : function()
28829     {
28830         this.iconEl.show();
28831         
28832         this.iconEl.removeClass(this.invalidClass);
28833         
28834         this.iconEl.addClass(this.validClass);
28835         
28836         this.fireEvent('valid', this);
28837     },
28838     
28839     /**
28840      * Mark this field as invalid
28841      * @param {String} msg The validation message
28842      */
28843     markInvalid : function(msg)
28844     {
28845         this.iconEl.show();
28846         
28847         this.iconEl.removeClass(this.validClass);
28848         
28849         this.iconEl.addClass(this.invalidClass);
28850         
28851         this.fireEvent('invalid', this, msg);
28852     }
28853     
28854    
28855 });
28856
28857 Roo.apply(Roo.bootstrap.FieldLabel, {
28858     
28859     groups: {},
28860     
28861      /**
28862     * register a FieldLabel Group
28863     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28864     */
28865     register : function(label)
28866     {
28867         if(this.groups.hasOwnProperty(label.target)){
28868             return;
28869         }
28870      
28871         this.groups[label.target] = label;
28872         
28873     },
28874     /**
28875     * fetch a FieldLabel Group based on the target
28876     * @param {string} target
28877     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28878     */
28879     get: function(target) {
28880         if (typeof(this.groups[target]) == 'undefined') {
28881             return false;
28882         }
28883         
28884         return this.groups[target] ;
28885     }
28886 });
28887
28888  
28889
28890  /*
28891  * - LGPL
28892  *
28893  * page DateSplitField.
28894  * 
28895  */
28896
28897
28898 /**
28899  * @class Roo.bootstrap.DateSplitField
28900  * @extends Roo.bootstrap.Component
28901  * Bootstrap DateSplitField class
28902  * @cfg {string} fieldLabel - the label associated
28903  * @cfg {Number} labelWidth set the width of label (0-12)
28904  * @cfg {String} labelAlign (top|left)
28905  * @cfg {Boolean} dayAllowBlank (true|false) default false
28906  * @cfg {Boolean} monthAllowBlank (true|false) default false
28907  * @cfg {Boolean} yearAllowBlank (true|false) default false
28908  * @cfg {string} dayPlaceholder 
28909  * @cfg {string} monthPlaceholder
28910  * @cfg {string} yearPlaceholder
28911  * @cfg {string} dayFormat default 'd'
28912  * @cfg {string} monthFormat default 'm'
28913  * @cfg {string} yearFormat default 'Y'
28914
28915  *     
28916  * @constructor
28917  * Create a new DateSplitField
28918  * @param {Object} config The config object
28919  */
28920
28921 Roo.bootstrap.DateSplitField = function(config){
28922     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28923     
28924     this.addEvents({
28925         // raw events
28926          /**
28927          * @event years
28928          * getting the data of years
28929          * @param {Roo.bootstrap.DateSplitField} this
28930          * @param {Object} years
28931          */
28932         "years" : true,
28933         /**
28934          * @event days
28935          * getting the data of days
28936          * @param {Roo.bootstrap.DateSplitField} this
28937          * @param {Object} days
28938          */
28939         "days" : true,
28940         /**
28941          * @event invalid
28942          * Fires after the field has been marked as invalid.
28943          * @param {Roo.form.Field} this
28944          * @param {String} msg The validation message
28945          */
28946         invalid : true,
28947        /**
28948          * @event valid
28949          * Fires after the field has been validated with no errors.
28950          * @param {Roo.form.Field} this
28951          */
28952         valid : true
28953     });
28954 };
28955
28956 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
28957     
28958     fieldLabel : '',
28959     labelAlign : 'top',
28960     labelWidth : 3,
28961     dayAllowBlank : false,
28962     monthAllowBlank : false,
28963     yearAllowBlank : false,
28964     dayPlaceholder : '',
28965     monthPlaceholder : '',
28966     yearPlaceholder : '',
28967     dayFormat : 'd',
28968     monthFormat : 'm',
28969     yearFormat : 'Y',
28970     isFormField : true,
28971     
28972     getAutoCreate : function()
28973     {
28974         var cfg = {
28975             tag : 'div',
28976             cls : 'row roo-date-split-field-group',
28977             cn : [
28978                 {
28979                     tag : 'input',
28980                     type : 'hidden',
28981                     cls : 'form-hidden-field roo-date-split-field-group-value',
28982                     name : this.name
28983                 }
28984             ]
28985         };
28986         
28987         if(this.fieldLabel){
28988             cfg.cn.push({
28989                 tag : 'div',
28990                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28991                 cn : [
28992                     {
28993                         tag : 'label',
28994                         html : this.fieldLabel
28995                     }
28996                 ]
28997             });
28998         }
28999         
29000         Roo.each(['day', 'month', 'year'], function(t){
29001             cfg.cn.push({
29002                 tag : 'div',
29003                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
29004             });
29005         }, this);
29006         
29007         return cfg;
29008     },
29009     
29010     inputEl: function ()
29011     {
29012         return this.el.select('.roo-date-split-field-group-value', true).first();
29013     },
29014     
29015     onRender : function(ct, position) 
29016     {
29017         var _this = this;
29018         
29019         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29020         
29021         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29022         
29023         this.dayField = new Roo.bootstrap.ComboBox({
29024             allowBlank : this.dayAllowBlank,
29025             alwaysQuery : true,
29026             displayField : 'value',
29027             editable : false,
29028             fieldLabel : '',
29029             forceSelection : true,
29030             mode : 'local',
29031             placeholder : this.dayPlaceholder,
29032             selectOnFocus : true,
29033             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29034             triggerAction : 'all',
29035             typeAhead : true,
29036             valueField : 'value',
29037             store : new Roo.data.SimpleStore({
29038                 data : (function() {    
29039                     var days = [];
29040                     _this.fireEvent('days', _this, days);
29041                     return days;
29042                 })(),
29043                 fields : [ 'value' ]
29044             }),
29045             listeners : {
29046                 select : function (_self, record, index)
29047                 {
29048                     _this.setValue(_this.getValue());
29049                 }
29050             }
29051         });
29052
29053         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29054         
29055         this.monthField = new Roo.bootstrap.MonthField({
29056             after : '<i class=\"fa fa-calendar\"></i>',
29057             allowBlank : this.monthAllowBlank,
29058             placeholder : this.monthPlaceholder,
29059             readOnly : true,
29060             listeners : {
29061                 render : function (_self)
29062                 {
29063                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
29064                         e.preventDefault();
29065                         _self.focus();
29066                     });
29067                 },
29068                 select : function (_self, oldvalue, newvalue)
29069                 {
29070                     _this.setValue(_this.getValue());
29071                 }
29072             }
29073         });
29074         
29075         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29076         
29077         this.yearField = new Roo.bootstrap.ComboBox({
29078             allowBlank : this.yearAllowBlank,
29079             alwaysQuery : true,
29080             displayField : 'value',
29081             editable : false,
29082             fieldLabel : '',
29083             forceSelection : true,
29084             mode : 'local',
29085             placeholder : this.yearPlaceholder,
29086             selectOnFocus : true,
29087             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29088             triggerAction : 'all',
29089             typeAhead : true,
29090             valueField : 'value',
29091             store : new Roo.data.SimpleStore({
29092                 data : (function() {
29093                     var years = [];
29094                     _this.fireEvent('years', _this, years);
29095                     return years;
29096                 })(),
29097                 fields : [ 'value' ]
29098             }),
29099             listeners : {
29100                 select : function (_self, record, index)
29101                 {
29102                     _this.setValue(_this.getValue());
29103                 }
29104             }
29105         });
29106
29107         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29108     },
29109     
29110     setValue : function(v, format)
29111     {
29112         this.inputEl.dom.value = v;
29113         
29114         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29115         
29116         var d = Date.parseDate(v, f);
29117         
29118         if(!d){
29119             this.validate();
29120             return;
29121         }
29122         
29123         this.setDay(d.format(this.dayFormat));
29124         this.setMonth(d.format(this.monthFormat));
29125         this.setYear(d.format(this.yearFormat));
29126         
29127         this.validate();
29128         
29129         return;
29130     },
29131     
29132     setDay : function(v)
29133     {
29134         this.dayField.setValue(v);
29135         this.inputEl.dom.value = this.getValue();
29136         this.validate();
29137         return;
29138     },
29139     
29140     setMonth : function(v)
29141     {
29142         this.monthField.setValue(v, true);
29143         this.inputEl.dom.value = this.getValue();
29144         this.validate();
29145         return;
29146     },
29147     
29148     setYear : function(v)
29149     {
29150         this.yearField.setValue(v);
29151         this.inputEl.dom.value = this.getValue();
29152         this.validate();
29153         return;
29154     },
29155     
29156     getDay : function()
29157     {
29158         return this.dayField.getValue();
29159     },
29160     
29161     getMonth : function()
29162     {
29163         return this.monthField.getValue();
29164     },
29165     
29166     getYear : function()
29167     {
29168         return this.yearField.getValue();
29169     },
29170     
29171     getValue : function()
29172     {
29173         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
29174         
29175         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
29176         
29177         return date;
29178     },
29179     
29180     reset : function()
29181     {
29182         this.setDay('');
29183         this.setMonth('');
29184         this.setYear('');
29185         this.inputEl.dom.value = '';
29186         this.validate();
29187         return;
29188     },
29189     
29190     validate : function()
29191     {
29192         var d = this.dayField.validate();
29193         var m = this.monthField.validate();
29194         var y = this.yearField.validate();
29195         
29196         var valid = true;
29197         
29198         if(
29199                 (!this.dayAllowBlank && !d) ||
29200                 (!this.monthAllowBlank && !m) ||
29201                 (!this.yearAllowBlank && !y)
29202         ){
29203             valid = false;
29204         }
29205         
29206         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
29207             return valid;
29208         }
29209         
29210         if(valid){
29211             this.markValid();
29212             return valid;
29213         }
29214         
29215         this.markInvalid();
29216         
29217         return valid;
29218     },
29219     
29220     markValid : function()
29221     {
29222         
29223         var label = this.el.select('label', true).first();
29224         var icon = this.el.select('i.fa-star', true).first();
29225
29226         if(label && icon){
29227             icon.remove();
29228         }
29229         
29230         this.fireEvent('valid', this);
29231     },
29232     
29233      /**
29234      * Mark this field as invalid
29235      * @param {String} msg The validation message
29236      */
29237     markInvalid : function(msg)
29238     {
29239         
29240         var label = this.el.select('label', true).first();
29241         var icon = this.el.select('i.fa-star', true).first();
29242
29243         if(label && !icon){
29244             this.el.select('.roo-date-split-field-label', true).createChild({
29245                 tag : 'i',
29246                 cls : 'text-danger fa fa-lg fa-star',
29247                 tooltip : 'This field is required',
29248                 style : 'margin-right:5px;'
29249             }, label, true);
29250         }
29251         
29252         this.fireEvent('invalid', this, msg);
29253     },
29254     
29255     clearInvalid : function()
29256     {
29257         var label = this.el.select('label', true).first();
29258         var icon = this.el.select('i.fa-star', true).first();
29259
29260         if(label && icon){
29261             icon.remove();
29262         }
29263         
29264         this.fireEvent('valid', this);
29265     },
29266     
29267     getName: function()
29268     {
29269         return this.name;
29270     }
29271     
29272 });
29273
29274  /**
29275  *
29276  * This is based on 
29277  * http://masonry.desandro.com
29278  *
29279  * The idea is to render all the bricks based on vertical width...
29280  *
29281  * The original code extends 'outlayer' - we might need to use that....
29282  * 
29283  */
29284
29285
29286 /**
29287  * @class Roo.bootstrap.LayoutMasonry
29288  * @extends Roo.bootstrap.Component
29289  * Bootstrap Layout Masonry class
29290  * 
29291  * @constructor
29292  * Create a new Element
29293  * @param {Object} config The config object
29294  */
29295
29296 Roo.bootstrap.LayoutMasonry = function(config){
29297     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
29298     
29299     this.bricks = [];
29300     
29301 };
29302
29303 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
29304     
29305     /**
29306      * @cfg {Boolean} isLayoutInstant = no animation?
29307      */   
29308     isLayoutInstant : false, // needed?
29309    
29310     /**
29311      * @cfg {Number} boxWidth  width of the columns
29312      */   
29313     boxWidth : 450,
29314     
29315       /**
29316      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
29317      */   
29318     boxHeight : 0,
29319     
29320     /**
29321      * @cfg {Number} padWidth padding below box..
29322      */   
29323     padWidth : 10, 
29324     
29325     /**
29326      * @cfg {Number} gutter gutter width..
29327      */   
29328     gutter : 10,
29329     
29330      /**
29331      * @cfg {Number} maxCols maximum number of columns
29332      */   
29333     
29334     maxCols: 0,
29335     
29336     /**
29337      * @cfg {Boolean} isAutoInitial defalut true
29338      */   
29339     isAutoInitial : true, 
29340     
29341     containerWidth: 0,
29342     
29343     /**
29344      * @cfg {Boolean} isHorizontal defalut false
29345      */   
29346     isHorizontal : false, 
29347
29348     currentSize : null,
29349     
29350     tag: 'div',
29351     
29352     cls: '',
29353     
29354     bricks: null, //CompositeElement
29355     
29356     cols : 1,
29357     
29358     _isLayoutInited : false,
29359     
29360 //    isAlternative : false, // only use for vertical layout...
29361     
29362     /**
29363      * @cfg {Number} alternativePadWidth padding below box..
29364      */   
29365     alternativePadWidth : 50, 
29366     
29367     getAutoCreate : function(){
29368         
29369         var cfg = {
29370             tag: this.tag,
29371             cls: 'blog-masonary-wrapper ' + this.cls,
29372             cn : {
29373                 cls : 'mas-boxes masonary'
29374             }
29375         };
29376         
29377         return cfg;
29378     },
29379     
29380     getChildContainer: function( )
29381     {
29382         if (this.boxesEl) {
29383             return this.boxesEl;
29384         }
29385         
29386         this.boxesEl = this.el.select('.mas-boxes').first();
29387         
29388         return this.boxesEl;
29389     },
29390     
29391     
29392     initEvents : function()
29393     {
29394         var _this = this;
29395         
29396         if(this.isAutoInitial){
29397             Roo.log('hook children rendered');
29398             this.on('childrenrendered', function() {
29399                 Roo.log('children rendered');
29400                 _this.initial();
29401             } ,this);
29402         }
29403     },
29404     
29405     initial : function()
29406     {
29407         this.currentSize = this.el.getBox(true);
29408         
29409         Roo.EventManager.onWindowResize(this.resize, this); 
29410
29411         if(!this.isAutoInitial){
29412             this.layout();
29413             return;
29414         }
29415         
29416         this.layout();
29417         
29418         return;
29419         //this.layout.defer(500,this);
29420         
29421     },
29422     
29423     resize : function()
29424     {
29425         Roo.log('resize');
29426         
29427         var cs = this.el.getBox(true);
29428         
29429         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29430             Roo.log("no change in with or X");
29431             return;
29432         }
29433         
29434         this.currentSize = cs;
29435         
29436         this.layout();
29437         
29438     },
29439     
29440     layout : function()
29441     {   
29442         this._resetLayout();
29443         
29444         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29445         
29446         this.layoutItems( isInstant );
29447       
29448         this._isLayoutInited = true;
29449         
29450     },
29451     
29452     _resetLayout : function()
29453     {
29454         if(this.isHorizontal){
29455             this.horizontalMeasureColumns();
29456             return;
29457         }
29458         
29459         this.verticalMeasureColumns();
29460         
29461     },
29462     
29463     verticalMeasureColumns : function()
29464     {
29465         this.getContainerWidth();
29466         
29467 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29468 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
29469 //            return;
29470 //        }
29471         
29472         var boxWidth = this.boxWidth + this.padWidth;
29473         
29474         if(this.containerWidth < this.boxWidth){
29475             boxWidth = this.containerWidth
29476         }
29477         
29478         var containerWidth = this.containerWidth;
29479         
29480         var cols = Math.floor(containerWidth / boxWidth);
29481         
29482         this.cols = Math.max( cols, 1 );
29483         
29484         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29485         
29486         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29487         
29488         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29489         
29490         this.colWidth = boxWidth + avail - this.padWidth;
29491         
29492         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29493         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
29494     },
29495     
29496     horizontalMeasureColumns : function()
29497     {
29498         this.getContainerWidth();
29499         
29500         var boxWidth = this.boxWidth;
29501         
29502         if(this.containerWidth < boxWidth){
29503             boxWidth = this.containerWidth;
29504         }
29505         
29506         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29507         
29508         this.el.setHeight(boxWidth);
29509         
29510     },
29511     
29512     getContainerWidth : function()
29513     {
29514         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
29515     },
29516     
29517     layoutItems : function( isInstant )
29518     {
29519         var items = Roo.apply([], this.bricks);
29520         
29521         if(this.isHorizontal){
29522             this._horizontalLayoutItems( items , isInstant );
29523             return;
29524         }
29525         
29526 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29527 //            this._verticalAlternativeLayoutItems( items , isInstant );
29528 //            return;
29529 //        }
29530         
29531         this._verticalLayoutItems( items , isInstant );
29532         
29533     },
29534     
29535     _verticalLayoutItems : function ( items , isInstant)
29536     {
29537         if ( !items || !items.length ) {
29538             return;
29539         }
29540         
29541         var standard = [
29542             ['xs', 'xs', 'xs', 'tall'],
29543             ['xs', 'xs', 'tall'],
29544             ['xs', 'xs', 'sm'],
29545             ['xs', 'xs', 'xs'],
29546             ['xs', 'tall'],
29547             ['xs', 'sm'],
29548             ['xs', 'xs'],
29549             ['xs'],
29550             
29551             ['sm', 'xs', 'xs'],
29552             ['sm', 'xs'],
29553             ['sm'],
29554             
29555             ['tall', 'xs', 'xs', 'xs'],
29556             ['tall', 'xs', 'xs'],
29557             ['tall', 'xs'],
29558             ['tall']
29559             
29560         ];
29561         
29562         var queue = [];
29563         
29564         var boxes = [];
29565         
29566         var box = [];
29567         
29568         Roo.each(items, function(item, k){
29569             
29570             switch (item.size) {
29571                 // these layouts take up a full box,
29572                 case 'md' :
29573                 case 'md-left' :
29574                 case 'md-right' :
29575                 case 'wide' :
29576                     
29577                     if(box.length){
29578                         boxes.push(box);
29579                         box = [];
29580                     }
29581                     
29582                     boxes.push([item]);
29583                     
29584                     break;
29585                     
29586                 case 'xs' :
29587                 case 'sm' :
29588                 case 'tall' :
29589                     
29590                     box.push(item);
29591                     
29592                     break;
29593                 default :
29594                     break;
29595                     
29596             }
29597             
29598         }, this);
29599         
29600         if(box.length){
29601             boxes.push(box);
29602             box = [];
29603         }
29604         
29605         var filterPattern = function(box, length)
29606         {
29607             if(!box.length){
29608                 return;
29609             }
29610             
29611             var match = false;
29612             
29613             var pattern = box.slice(0, length);
29614             
29615             var format = [];
29616             
29617             Roo.each(pattern, function(i){
29618                 format.push(i.size);
29619             }, this);
29620             
29621             Roo.each(standard, function(s){
29622                 
29623                 if(String(s) != String(format)){
29624                     return;
29625                 }
29626                 
29627                 match = true;
29628                 return false;
29629                 
29630             }, this);
29631             
29632             if(!match && length == 1){
29633                 return;
29634             }
29635             
29636             if(!match){
29637                 filterPattern(box, length - 1);
29638                 return;
29639             }
29640                 
29641             queue.push(pattern);
29642
29643             box = box.slice(length, box.length);
29644
29645             filterPattern(box, 4);
29646
29647             return;
29648             
29649         }
29650         
29651         Roo.each(boxes, function(box, k){
29652             
29653             if(!box.length){
29654                 return;
29655             }
29656             
29657             if(box.length == 1){
29658                 queue.push(box);
29659                 return;
29660             }
29661             
29662             filterPattern(box, 4);
29663             
29664         }, this);
29665         
29666         this._processVerticalLayoutQueue( queue, isInstant );
29667         
29668     },
29669     
29670 //    _verticalAlternativeLayoutItems : function( items , isInstant )
29671 //    {
29672 //        if ( !items || !items.length ) {
29673 //            return;
29674 //        }
29675 //
29676 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
29677 //        
29678 //    },
29679     
29680     _horizontalLayoutItems : function ( items , isInstant)
29681     {
29682         if ( !items || !items.length || items.length < 3) {
29683             return;
29684         }
29685         
29686         items.reverse();
29687         
29688         var eItems = items.slice(0, 3);
29689         
29690         items = items.slice(3, items.length);
29691         
29692         var standard = [
29693             ['xs', 'xs', 'xs', 'wide'],
29694             ['xs', 'xs', 'wide'],
29695             ['xs', 'xs', 'sm'],
29696             ['xs', 'xs', 'xs'],
29697             ['xs', 'wide'],
29698             ['xs', 'sm'],
29699             ['xs', 'xs'],
29700             ['xs'],
29701             
29702             ['sm', 'xs', 'xs'],
29703             ['sm', 'xs'],
29704             ['sm'],
29705             
29706             ['wide', 'xs', 'xs', 'xs'],
29707             ['wide', 'xs', 'xs'],
29708             ['wide', 'xs'],
29709             ['wide'],
29710             
29711             ['wide-thin']
29712         ];
29713         
29714         var queue = [];
29715         
29716         var boxes = [];
29717         
29718         var box = [];
29719         
29720         Roo.each(items, function(item, k){
29721             
29722             switch (item.size) {
29723                 case 'md' :
29724                 case 'md-left' :
29725                 case 'md-right' :
29726                 case 'tall' :
29727                     
29728                     if(box.length){
29729                         boxes.push(box);
29730                         box = [];
29731                     }
29732                     
29733                     boxes.push([item]);
29734                     
29735                     break;
29736                     
29737                 case 'xs' :
29738                 case 'sm' :
29739                 case 'wide' :
29740                 case 'wide-thin' :
29741                     
29742                     box.push(item);
29743                     
29744                     break;
29745                 default :
29746                     break;
29747                     
29748             }
29749             
29750         }, this);
29751         
29752         if(box.length){
29753             boxes.push(box);
29754             box = [];
29755         }
29756         
29757         var filterPattern = function(box, length)
29758         {
29759             if(!box.length){
29760                 return;
29761             }
29762             
29763             var match = false;
29764             
29765             var pattern = box.slice(0, length);
29766             
29767             var format = [];
29768             
29769             Roo.each(pattern, function(i){
29770                 format.push(i.size);
29771             }, this);
29772             
29773             Roo.each(standard, function(s){
29774                 
29775                 if(String(s) != String(format)){
29776                     return;
29777                 }
29778                 
29779                 match = true;
29780                 return false;
29781                 
29782             }, this);
29783             
29784             if(!match && length == 1){
29785                 return;
29786             }
29787             
29788             if(!match){
29789                 filterPattern(box, length - 1);
29790                 return;
29791             }
29792                 
29793             queue.push(pattern);
29794
29795             box = box.slice(length, box.length);
29796
29797             filterPattern(box, 4);
29798
29799             return;
29800             
29801         }
29802         
29803         Roo.each(boxes, function(box, k){
29804             
29805             if(!box.length){
29806                 return;
29807             }
29808             
29809             if(box.length == 1){
29810                 queue.push(box);
29811                 return;
29812             }
29813             
29814             filterPattern(box, 4);
29815             
29816         }, this);
29817         
29818         
29819         var prune = [];
29820         
29821         var pos = this.el.getBox(true);
29822         
29823         var minX = pos.x;
29824         
29825         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29826         
29827         var hit_end = false;
29828         
29829         Roo.each(queue, function(box){
29830             
29831             if(hit_end){
29832                 
29833                 Roo.each(box, function(b){
29834                 
29835                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29836                     b.el.hide();
29837
29838                 }, this);
29839
29840                 return;
29841             }
29842             
29843             var mx = 0;
29844             
29845             Roo.each(box, function(b){
29846                 
29847                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29848                 b.el.show();
29849
29850                 mx = Math.max(mx, b.x);
29851                 
29852             }, this);
29853             
29854             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29855             
29856             if(maxX < minX){
29857                 
29858                 Roo.each(box, function(b){
29859                 
29860                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29861                     b.el.hide();
29862                     
29863                 }, this);
29864                 
29865                 hit_end = true;
29866                 
29867                 return;
29868             }
29869             
29870             prune.push(box);
29871             
29872         }, this);
29873         
29874         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29875     },
29876     
29877     /** Sets position of item in DOM
29878     * @param {Element} item
29879     * @param {Number} x - horizontal position
29880     * @param {Number} y - vertical position
29881     * @param {Boolean} isInstant - disables transitions
29882     */
29883     _processVerticalLayoutQueue : function( queue, isInstant )
29884     {
29885         var pos = this.el.getBox(true);
29886         var x = pos.x;
29887         var y = pos.y;
29888         var maxY = [];
29889         
29890         for (var i = 0; i < this.cols; i++){
29891             maxY[i] = pos.y;
29892         }
29893         
29894         Roo.each(queue, function(box, k){
29895             
29896             var col = k % this.cols;
29897             
29898             Roo.each(box, function(b,kk){
29899                 
29900                 b.el.position('absolute');
29901                 
29902                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29903                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29904                 
29905                 if(b.size == 'md-left' || b.size == 'md-right'){
29906                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29907                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29908                 }
29909                 
29910                 b.el.setWidth(width);
29911                 b.el.setHeight(height);
29912                 // iframe?
29913                 b.el.select('iframe',true).setSize(width,height);
29914                 
29915             }, this);
29916             
29917             for (var i = 0; i < this.cols; i++){
29918                 
29919                 if(maxY[i] < maxY[col]){
29920                     col = i;
29921                     continue;
29922                 }
29923                 
29924                 col = Math.min(col, i);
29925                 
29926             }
29927             
29928             x = pos.x + col * (this.colWidth + this.padWidth);
29929             
29930             y = maxY[col];
29931             
29932             var positions = [];
29933             
29934             switch (box.length){
29935                 case 1 :
29936                     positions = this.getVerticalOneBoxColPositions(x, y, box);
29937                     break;
29938                 case 2 :
29939                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
29940                     break;
29941                 case 3 :
29942                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
29943                     break;
29944                 case 4 :
29945                     positions = this.getVerticalFourBoxColPositions(x, y, box);
29946                     break;
29947                 default :
29948                     break;
29949             }
29950             
29951             Roo.each(box, function(b,kk){
29952                 
29953                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29954                 
29955                 var sz = b.el.getSize();
29956                 
29957                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29958                 
29959             }, this);
29960             
29961         }, this);
29962         
29963         var mY = 0;
29964         
29965         for (var i = 0; i < this.cols; i++){
29966             mY = Math.max(mY, maxY[i]);
29967         }
29968         
29969         this.el.setHeight(mY - pos.y);
29970         
29971     },
29972     
29973 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29974 //    {
29975 //        var pos = this.el.getBox(true);
29976 //        var x = pos.x;
29977 //        var y = pos.y;
29978 //        var maxX = pos.right;
29979 //        
29980 //        var maxHeight = 0;
29981 //        
29982 //        Roo.each(items, function(item, k){
29983 //            
29984 //            var c = k % 2;
29985 //            
29986 //            item.el.position('absolute');
29987 //                
29988 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29989 //
29990 //            item.el.setWidth(width);
29991 //
29992 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29993 //
29994 //            item.el.setHeight(height);
29995 //            
29996 //            if(c == 0){
29997 //                item.el.setXY([x, y], isInstant ? false : true);
29998 //            } else {
29999 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
30000 //            }
30001 //            
30002 //            y = y + height + this.alternativePadWidth;
30003 //            
30004 //            maxHeight = maxHeight + height + this.alternativePadWidth;
30005 //            
30006 //        }, this);
30007 //        
30008 //        this.el.setHeight(maxHeight);
30009 //        
30010 //    },
30011     
30012     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30013     {
30014         var pos = this.el.getBox(true);
30015         
30016         var minX = pos.x;
30017         var minY = pos.y;
30018         
30019         var maxX = pos.right;
30020         
30021         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
30022         
30023         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30024         
30025         Roo.each(queue, function(box, k){
30026             
30027             Roo.each(box, function(b, kk){
30028                 
30029                 b.el.position('absolute');
30030                 
30031                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30032                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30033                 
30034                 if(b.size == 'md-left' || b.size == 'md-right'){
30035                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30036                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30037                 }
30038                 
30039                 b.el.setWidth(width);
30040                 b.el.setHeight(height);
30041                 
30042             }, this);
30043             
30044             if(!box.length){
30045                 return;
30046             }
30047             
30048             var positions = [];
30049             
30050             switch (box.length){
30051                 case 1 :
30052                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30053                     break;
30054                 case 2 :
30055                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30056                     break;
30057                 case 3 :
30058                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30059                     break;
30060                 case 4 :
30061                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30062                     break;
30063                 default :
30064                     break;
30065             }
30066             
30067             Roo.each(box, function(b,kk){
30068                 
30069                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30070                 
30071                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30072                 
30073             }, this);
30074             
30075         }, this);
30076         
30077     },
30078     
30079     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30080     {
30081         Roo.each(eItems, function(b,k){
30082             
30083             b.size = (k == 0) ? 'sm' : 'xs';
30084             b.x = (k == 0) ? 2 : 1;
30085             b.y = (k == 0) ? 2 : 1;
30086             
30087             b.el.position('absolute');
30088             
30089             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30090                 
30091             b.el.setWidth(width);
30092             
30093             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30094             
30095             b.el.setHeight(height);
30096             
30097         }, this);
30098
30099         var positions = [];
30100         
30101         positions.push({
30102             x : maxX - this.unitWidth * 2 - this.gutter,
30103             y : minY
30104         });
30105         
30106         positions.push({
30107             x : maxX - this.unitWidth,
30108             y : minY + (this.unitWidth + this.gutter) * 2
30109         });
30110         
30111         positions.push({
30112             x : maxX - this.unitWidth * 3 - this.gutter * 2,
30113             y : minY
30114         });
30115         
30116         Roo.each(eItems, function(b,k){
30117             
30118             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
30119
30120         }, this);
30121         
30122     },
30123     
30124     getVerticalOneBoxColPositions : function(x, y, box)
30125     {
30126         var pos = [];
30127         
30128         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
30129         
30130         if(box[0].size == 'md-left'){
30131             rand = 0;
30132         }
30133         
30134         if(box[0].size == 'md-right'){
30135             rand = 1;
30136         }
30137         
30138         pos.push({
30139             x : x + (this.unitWidth + this.gutter) * rand,
30140             y : y
30141         });
30142         
30143         return pos;
30144     },
30145     
30146     getVerticalTwoBoxColPositions : function(x, y, box)
30147     {
30148         var pos = [];
30149         
30150         if(box[0].size == 'xs'){
30151             
30152             pos.push({
30153                 x : x,
30154                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
30155             });
30156
30157             pos.push({
30158                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
30159                 y : y
30160             });
30161             
30162             return pos;
30163             
30164         }
30165         
30166         pos.push({
30167             x : x,
30168             y : y
30169         });
30170
30171         pos.push({
30172             x : x + (this.unitWidth + this.gutter) * 2,
30173             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
30174         });
30175         
30176         return pos;
30177         
30178     },
30179     
30180     getVerticalThreeBoxColPositions : function(x, y, box)
30181     {
30182         var pos = [];
30183         
30184         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30185             
30186             pos.push({
30187                 x : x,
30188                 y : y
30189             });
30190
30191             pos.push({
30192                 x : x + (this.unitWidth + this.gutter) * 1,
30193                 y : y
30194             });
30195             
30196             pos.push({
30197                 x : x + (this.unitWidth + this.gutter) * 2,
30198                 y : y
30199             });
30200             
30201             return pos;
30202             
30203         }
30204         
30205         if(box[0].size == 'xs' && box[1].size == 'xs'){
30206             
30207             pos.push({
30208                 x : x,
30209                 y : y
30210             });
30211
30212             pos.push({
30213                 x : x,
30214                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
30215             });
30216             
30217             pos.push({
30218                 x : x + (this.unitWidth + this.gutter) * 1,
30219                 y : y
30220             });
30221             
30222             return pos;
30223             
30224         }
30225         
30226         pos.push({
30227             x : x,
30228             y : y
30229         });
30230
30231         pos.push({
30232             x : x + (this.unitWidth + this.gutter) * 2,
30233             y : y
30234         });
30235
30236         pos.push({
30237             x : x + (this.unitWidth + this.gutter) * 2,
30238             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
30239         });
30240             
30241         return pos;
30242         
30243     },
30244     
30245     getVerticalFourBoxColPositions : function(x, y, box)
30246     {
30247         var pos = [];
30248         
30249         if(box[0].size == 'xs'){
30250             
30251             pos.push({
30252                 x : x,
30253                 y : y
30254             });
30255
30256             pos.push({
30257                 x : x,
30258                 y : y + (this.unitHeight + this.gutter) * 1
30259             });
30260             
30261             pos.push({
30262                 x : x,
30263                 y : y + (this.unitHeight + this.gutter) * 2
30264             });
30265             
30266             pos.push({
30267                 x : x + (this.unitWidth + this.gutter) * 1,
30268                 y : y
30269             });
30270             
30271             return pos;
30272             
30273         }
30274         
30275         pos.push({
30276             x : x,
30277             y : y
30278         });
30279
30280         pos.push({
30281             x : x + (this.unitWidth + this.gutter) * 2,
30282             y : y
30283         });
30284
30285         pos.push({
30286             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
30287             y : y + (this.unitHeight + this.gutter) * 1
30288         });
30289
30290         pos.push({
30291             x : x + (this.unitWidth + this.gutter) * 2,
30292             y : y + (this.unitWidth + this.gutter) * 2
30293         });
30294
30295         return pos;
30296         
30297     },
30298     
30299     getHorizontalOneBoxColPositions : function(maxX, minY, box)
30300     {
30301         var pos = [];
30302         
30303         if(box[0].size == 'md-left'){
30304             pos.push({
30305                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30306                 y : minY
30307             });
30308             
30309             return pos;
30310         }
30311         
30312         if(box[0].size == 'md-right'){
30313             pos.push({
30314                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30315                 y : minY + (this.unitWidth + this.gutter) * 1
30316             });
30317             
30318             return pos;
30319         }
30320         
30321         var rand = Math.floor(Math.random() * (4 - box[0].y));
30322         
30323         pos.push({
30324             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30325             y : minY + (this.unitWidth + this.gutter) * rand
30326         });
30327         
30328         return pos;
30329         
30330     },
30331     
30332     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
30333     {
30334         var pos = [];
30335         
30336         if(box[0].size == 'xs'){
30337             
30338             pos.push({
30339                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30340                 y : minY
30341             });
30342
30343             pos.push({
30344                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30345                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
30346             });
30347             
30348             return pos;
30349             
30350         }
30351         
30352         pos.push({
30353             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30354             y : minY
30355         });
30356
30357         pos.push({
30358             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30359             y : minY + (this.unitWidth + this.gutter) * 2
30360         });
30361         
30362         return pos;
30363         
30364     },
30365     
30366     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30367     {
30368         var pos = [];
30369         
30370         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30371             
30372             pos.push({
30373                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30374                 y : minY
30375             });
30376
30377             pos.push({
30378                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30379                 y : minY + (this.unitWidth + this.gutter) * 1
30380             });
30381             
30382             pos.push({
30383                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30384                 y : minY + (this.unitWidth + this.gutter) * 2
30385             });
30386             
30387             return pos;
30388             
30389         }
30390         
30391         if(box[0].size == 'xs' && box[1].size == 'xs'){
30392             
30393             pos.push({
30394                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30395                 y : minY
30396             });
30397
30398             pos.push({
30399                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30400                 y : minY
30401             });
30402             
30403             pos.push({
30404                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30405                 y : minY + (this.unitWidth + this.gutter) * 1
30406             });
30407             
30408             return pos;
30409             
30410         }
30411         
30412         pos.push({
30413             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30414             y : minY
30415         });
30416
30417         pos.push({
30418             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30419             y : minY + (this.unitWidth + this.gutter) * 2
30420         });
30421
30422         pos.push({
30423             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30424             y : minY + (this.unitWidth + this.gutter) * 2
30425         });
30426             
30427         return pos;
30428         
30429     },
30430     
30431     getHorizontalFourBoxColPositions : function(maxX, minY, box)
30432     {
30433         var pos = [];
30434         
30435         if(box[0].size == 'xs'){
30436             
30437             pos.push({
30438                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30439                 y : minY
30440             });
30441
30442             pos.push({
30443                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30444                 y : minY
30445             });
30446             
30447             pos.push({
30448                 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),
30449                 y : minY
30450             });
30451             
30452             pos.push({
30453                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30454                 y : minY + (this.unitWidth + this.gutter) * 1
30455             });
30456             
30457             return pos;
30458             
30459         }
30460         
30461         pos.push({
30462             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30463             y : minY
30464         });
30465         
30466         pos.push({
30467             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30468             y : minY + (this.unitWidth + this.gutter) * 2
30469         });
30470         
30471         pos.push({
30472             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30473             y : minY + (this.unitWidth + this.gutter) * 2
30474         });
30475         
30476         pos.push({
30477             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),
30478             y : minY + (this.unitWidth + this.gutter) * 2
30479         });
30480
30481         return pos;
30482         
30483     }
30484     
30485 });
30486
30487  
30488
30489  /**
30490  *
30491  * This is based on 
30492  * http://masonry.desandro.com
30493  *
30494  * The idea is to render all the bricks based on vertical width...
30495  *
30496  * The original code extends 'outlayer' - we might need to use that....
30497  * 
30498  */
30499
30500
30501 /**
30502  * @class Roo.bootstrap.LayoutMasonryAuto
30503  * @extends Roo.bootstrap.Component
30504  * Bootstrap Layout Masonry class
30505  * 
30506  * @constructor
30507  * Create a new Element
30508  * @param {Object} config The config object
30509  */
30510
30511 Roo.bootstrap.LayoutMasonryAuto = function(config){
30512     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30513 };
30514
30515 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
30516     
30517       /**
30518      * @cfg {Boolean} isFitWidth  - resize the width..
30519      */   
30520     isFitWidth : false,  // options..
30521     /**
30522      * @cfg {Boolean} isOriginLeft = left align?
30523      */   
30524     isOriginLeft : true,
30525     /**
30526      * @cfg {Boolean} isOriginTop = top align?
30527      */   
30528     isOriginTop : false,
30529     /**
30530      * @cfg {Boolean} isLayoutInstant = no animation?
30531      */   
30532     isLayoutInstant : false, // needed?
30533     /**
30534      * @cfg {Boolean} isResizingContainer = not sure if this is used..
30535      */   
30536     isResizingContainer : true,
30537     /**
30538      * @cfg {Number} columnWidth  width of the columns 
30539      */   
30540     
30541     columnWidth : 0,
30542     
30543     /**
30544      * @cfg {Number} maxCols maximum number of columns
30545      */   
30546     
30547     maxCols: 0,
30548     /**
30549      * @cfg {Number} padHeight padding below box..
30550      */   
30551     
30552     padHeight : 10, 
30553     
30554     /**
30555      * @cfg {Boolean} isAutoInitial defalut true
30556      */   
30557     
30558     isAutoInitial : true, 
30559     
30560     // private?
30561     gutter : 0,
30562     
30563     containerWidth: 0,
30564     initialColumnWidth : 0,
30565     currentSize : null,
30566     
30567     colYs : null, // array.
30568     maxY : 0,
30569     padWidth: 10,
30570     
30571     
30572     tag: 'div',
30573     cls: '',
30574     bricks: null, //CompositeElement
30575     cols : 0, // array?
30576     // element : null, // wrapped now this.el
30577     _isLayoutInited : null, 
30578     
30579     
30580     getAutoCreate : function(){
30581         
30582         var cfg = {
30583             tag: this.tag,
30584             cls: 'blog-masonary-wrapper ' + this.cls,
30585             cn : {
30586                 cls : 'mas-boxes masonary'
30587             }
30588         };
30589         
30590         return cfg;
30591     },
30592     
30593     getChildContainer: function( )
30594     {
30595         if (this.boxesEl) {
30596             return this.boxesEl;
30597         }
30598         
30599         this.boxesEl = this.el.select('.mas-boxes').first();
30600         
30601         return this.boxesEl;
30602     },
30603     
30604     
30605     initEvents : function()
30606     {
30607         var _this = this;
30608         
30609         if(this.isAutoInitial){
30610             Roo.log('hook children rendered');
30611             this.on('childrenrendered', function() {
30612                 Roo.log('children rendered');
30613                 _this.initial();
30614             } ,this);
30615         }
30616         
30617     },
30618     
30619     initial : function()
30620     {
30621         this.reloadItems();
30622
30623         this.currentSize = this.el.getBox(true);
30624
30625         /// was window resize... - let's see if this works..
30626         Roo.EventManager.onWindowResize(this.resize, this); 
30627
30628         if(!this.isAutoInitial){
30629             this.layout();
30630             return;
30631         }
30632         
30633         this.layout.defer(500,this);
30634     },
30635     
30636     reloadItems: function()
30637     {
30638         this.bricks = this.el.select('.masonry-brick', true);
30639         
30640         this.bricks.each(function(b) {
30641             //Roo.log(b.getSize());
30642             if (!b.attr('originalwidth')) {
30643                 b.attr('originalwidth',  b.getSize().width);
30644             }
30645             
30646         });
30647         
30648         Roo.log(this.bricks.elements.length);
30649     },
30650     
30651     resize : function()
30652     {
30653         Roo.log('resize');
30654         var cs = this.el.getBox(true);
30655         
30656         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30657             Roo.log("no change in with or X");
30658             return;
30659         }
30660         this.currentSize = cs;
30661         this.layout();
30662     },
30663     
30664     layout : function()
30665     {
30666          Roo.log('layout');
30667         this._resetLayout();
30668         //this._manageStamps();
30669       
30670         // don't animate first layout
30671         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30672         this.layoutItems( isInstant );
30673       
30674         // flag for initalized
30675         this._isLayoutInited = true;
30676     },
30677     
30678     layoutItems : function( isInstant )
30679     {
30680         //var items = this._getItemsForLayout( this.items );
30681         // original code supports filtering layout items.. we just ignore it..
30682         
30683         this._layoutItems( this.bricks , isInstant );
30684       
30685         this._postLayout();
30686     },
30687     _layoutItems : function ( items , isInstant)
30688     {
30689        //this.fireEvent( 'layout', this, items );
30690     
30691
30692         if ( !items || !items.elements.length ) {
30693           // no items, emit event with empty array
30694             return;
30695         }
30696
30697         var queue = [];
30698         items.each(function(item) {
30699             Roo.log("layout item");
30700             Roo.log(item);
30701             // get x/y object from method
30702             var position = this._getItemLayoutPosition( item );
30703             // enqueue
30704             position.item = item;
30705             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30706             queue.push( position );
30707         }, this);
30708       
30709         this._processLayoutQueue( queue );
30710     },
30711     /** Sets position of item in DOM
30712     * @param {Element} item
30713     * @param {Number} x - horizontal position
30714     * @param {Number} y - vertical position
30715     * @param {Boolean} isInstant - disables transitions
30716     */
30717     _processLayoutQueue : function( queue )
30718     {
30719         for ( var i=0, len = queue.length; i < len; i++ ) {
30720             var obj = queue[i];
30721             obj.item.position('absolute');
30722             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30723         }
30724     },
30725       
30726     
30727     /**
30728     * Any logic you want to do after each layout,
30729     * i.e. size the container
30730     */
30731     _postLayout : function()
30732     {
30733         this.resizeContainer();
30734     },
30735     
30736     resizeContainer : function()
30737     {
30738         if ( !this.isResizingContainer ) {
30739             return;
30740         }
30741         var size = this._getContainerSize();
30742         if ( size ) {
30743             this.el.setSize(size.width,size.height);
30744             this.boxesEl.setSize(size.width,size.height);
30745         }
30746     },
30747     
30748     
30749     
30750     _resetLayout : function()
30751     {
30752         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30753         this.colWidth = this.el.getWidth();
30754         //this.gutter = this.el.getWidth(); 
30755         
30756         this.measureColumns();
30757
30758         // reset column Y
30759         var i = this.cols;
30760         this.colYs = [];
30761         while (i--) {
30762             this.colYs.push( 0 );
30763         }
30764     
30765         this.maxY = 0;
30766     },
30767
30768     measureColumns : function()
30769     {
30770         this.getContainerWidth();
30771       // if columnWidth is 0, default to outerWidth of first item
30772         if ( !this.columnWidth ) {
30773             var firstItem = this.bricks.first();
30774             Roo.log(firstItem);
30775             this.columnWidth  = this.containerWidth;
30776             if (firstItem && firstItem.attr('originalwidth') ) {
30777                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30778             }
30779             // columnWidth fall back to item of first element
30780             Roo.log("set column width?");
30781                         this.initialColumnWidth = this.columnWidth  ;
30782
30783             // if first elem has no width, default to size of container
30784             
30785         }
30786         
30787         
30788         if (this.initialColumnWidth) {
30789             this.columnWidth = this.initialColumnWidth;
30790         }
30791         
30792         
30793             
30794         // column width is fixed at the top - however if container width get's smaller we should
30795         // reduce it...
30796         
30797         // this bit calcs how man columns..
30798             
30799         var columnWidth = this.columnWidth += this.gutter;
30800       
30801         // calculate columns
30802         var containerWidth = this.containerWidth + this.gutter;
30803         
30804         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30805         // fix rounding errors, typically with gutters
30806         var excess = columnWidth - containerWidth % columnWidth;
30807         
30808         
30809         // if overshoot is less than a pixel, round up, otherwise floor it
30810         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30811         cols = Math[ mathMethod ]( cols );
30812         this.cols = Math.max( cols, 1 );
30813         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30814         
30815          // padding positioning..
30816         var totalColWidth = this.cols * this.columnWidth;
30817         var padavail = this.containerWidth - totalColWidth;
30818         // so for 2 columns - we need 3 'pads'
30819         
30820         var padNeeded = (1+this.cols) * this.padWidth;
30821         
30822         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30823         
30824         this.columnWidth += padExtra
30825         //this.padWidth = Math.floor(padavail /  ( this.cols));
30826         
30827         // adjust colum width so that padding is fixed??
30828         
30829         // we have 3 columns ... total = width * 3
30830         // we have X left over... that should be used by 
30831         
30832         //if (this.expandC) {
30833             
30834         //}
30835         
30836         
30837         
30838     },
30839     
30840     getContainerWidth : function()
30841     {
30842        /* // container is parent if fit width
30843         var container = this.isFitWidth ? this.element.parentNode : this.element;
30844         // check that this.size and size are there
30845         // IE8 triggers resize on body size change, so they might not be
30846         
30847         var size = getSize( container );  //FIXME
30848         this.containerWidth = size && size.innerWidth; //FIXME
30849         */
30850          
30851         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30852         
30853     },
30854     
30855     _getItemLayoutPosition : function( item )  // what is item?
30856     {
30857         // we resize the item to our columnWidth..
30858       
30859         item.setWidth(this.columnWidth);
30860         item.autoBoxAdjust  = false;
30861         
30862         var sz = item.getSize();
30863  
30864         // how many columns does this brick span
30865         var remainder = this.containerWidth % this.columnWidth;
30866         
30867         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30868         // round if off by 1 pixel, otherwise use ceil
30869         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
30870         colSpan = Math.min( colSpan, this.cols );
30871         
30872         // normally this should be '1' as we dont' currently allow multi width columns..
30873         
30874         var colGroup = this._getColGroup( colSpan );
30875         // get the minimum Y value from the columns
30876         var minimumY = Math.min.apply( Math, colGroup );
30877         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30878         
30879         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
30880          
30881         // position the brick
30882         var position = {
30883             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30884             y: this.currentSize.y + minimumY + this.padHeight
30885         };
30886         
30887         Roo.log(position);
30888         // apply setHeight to necessary columns
30889         var setHeight = minimumY + sz.height + this.padHeight;
30890         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30891         
30892         var setSpan = this.cols + 1 - colGroup.length;
30893         for ( var i = 0; i < setSpan; i++ ) {
30894           this.colYs[ shortColIndex + i ] = setHeight ;
30895         }
30896       
30897         return position;
30898     },
30899     
30900     /**
30901      * @param {Number} colSpan - number of columns the element spans
30902      * @returns {Array} colGroup
30903      */
30904     _getColGroup : function( colSpan )
30905     {
30906         if ( colSpan < 2 ) {
30907           // if brick spans only one column, use all the column Ys
30908           return this.colYs;
30909         }
30910       
30911         var colGroup = [];
30912         // how many different places could this brick fit horizontally
30913         var groupCount = this.cols + 1 - colSpan;
30914         // for each group potential horizontal position
30915         for ( var i = 0; i < groupCount; i++ ) {
30916           // make an array of colY values for that one group
30917           var groupColYs = this.colYs.slice( i, i + colSpan );
30918           // and get the max value of the array
30919           colGroup[i] = Math.max.apply( Math, groupColYs );
30920         }
30921         return colGroup;
30922     },
30923     /*
30924     _manageStamp : function( stamp )
30925     {
30926         var stampSize =  stamp.getSize();
30927         var offset = stamp.getBox();
30928         // get the columns that this stamp affects
30929         var firstX = this.isOriginLeft ? offset.x : offset.right;
30930         var lastX = firstX + stampSize.width;
30931         var firstCol = Math.floor( firstX / this.columnWidth );
30932         firstCol = Math.max( 0, firstCol );
30933         
30934         var lastCol = Math.floor( lastX / this.columnWidth );
30935         // lastCol should not go over if multiple of columnWidth #425
30936         lastCol -= lastX % this.columnWidth ? 0 : 1;
30937         lastCol = Math.min( this.cols - 1, lastCol );
30938         
30939         // set colYs to bottom of the stamp
30940         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30941             stampSize.height;
30942             
30943         for ( var i = firstCol; i <= lastCol; i++ ) {
30944           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30945         }
30946     },
30947     */
30948     
30949     _getContainerSize : function()
30950     {
30951         this.maxY = Math.max.apply( Math, this.colYs );
30952         var size = {
30953             height: this.maxY
30954         };
30955       
30956         if ( this.isFitWidth ) {
30957             size.width = this._getContainerFitWidth();
30958         }
30959       
30960         return size;
30961     },
30962     
30963     _getContainerFitWidth : function()
30964     {
30965         var unusedCols = 0;
30966         // count unused columns
30967         var i = this.cols;
30968         while ( --i ) {
30969           if ( this.colYs[i] !== 0 ) {
30970             break;
30971           }
30972           unusedCols++;
30973         }
30974         // fit container to columns that have been used
30975         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30976     },
30977     
30978     needsResizeLayout : function()
30979     {
30980         var previousWidth = this.containerWidth;
30981         this.getContainerWidth();
30982         return previousWidth !== this.containerWidth;
30983     }
30984  
30985 });
30986
30987  
30988
30989  /*
30990  * - LGPL
30991  *
30992  * element
30993  * 
30994  */
30995
30996 /**
30997  * @class Roo.bootstrap.MasonryBrick
30998  * @extends Roo.bootstrap.Component
30999  * Bootstrap MasonryBrick class
31000  * 
31001  * @constructor
31002  * Create a new MasonryBrick
31003  * @param {Object} config The config object
31004  */
31005
31006 Roo.bootstrap.MasonryBrick = function(config){
31007     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
31008     
31009     this.addEvents({
31010         // raw events
31011         /**
31012          * @event click
31013          * When a MasonryBrick is clcik
31014          * @param {Roo.bootstrap.MasonryBrick} this
31015          * @param {Roo.EventObject} e
31016          */
31017         "click" : true
31018     });
31019 };
31020
31021 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
31022     
31023     /**
31024      * @cfg {String} title
31025      */   
31026     title : '',
31027     /**
31028      * @cfg {String} html
31029      */   
31030     html : '',
31031     /**
31032      * @cfg {String} bgimage
31033      */   
31034     bgimage : '',
31035     /**
31036      * @cfg {String} videourl
31037      */   
31038     videourl : '',
31039     /**
31040      * @cfg {String} cls
31041      */   
31042     cls : '',
31043     /**
31044      * @cfg {String} href
31045      */   
31046     href : '',
31047     /**
31048      * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
31049      */   
31050     size : 'xs',
31051     
31052     /**
31053      * @cfg {String} (center|bottom) placetitle
31054      */   
31055     placetitle : '',
31056     
31057     /**
31058      * @cfg {Boolean} isFitContainer defalut true
31059      */   
31060     isFitContainer : true, 
31061     
31062     /**
31063      * @cfg {Boolean} preventDefault defalut false
31064      */   
31065     preventDefault : false, 
31066     
31067     getAutoCreate : function()
31068     {
31069         if(!this.isFitContainer){
31070             return this.getSplitAutoCreate();
31071         }
31072         
31073         var cls = 'masonry-brick masonry-brick-full';
31074         
31075         if(this.href.length){
31076             cls += ' masonry-brick-link';
31077         }
31078         
31079         if(this.bgimage.length){
31080             cls += ' masonry-brick-image';
31081         }
31082         
31083         if(!this.html.length){
31084             cls += ' enable-mask';
31085         }
31086         
31087         if(this.size){
31088             cls += ' masonry-' + this.size + '-brick';
31089         }
31090         
31091         if(this.placetitle.length){
31092             
31093             switch (this.placetitle) {
31094                 case 'center' :
31095                     cls += ' masonry-center-title';
31096                     break;
31097                 case 'bottom' :
31098                     cls += ' masonry-bottom-title';
31099                     break;
31100                 default:
31101                     break;
31102             }
31103             
31104         } else {
31105             if(!this.html.length && !this.bgimage.length){
31106                 cls += ' masonry-center-title';
31107             }
31108
31109             if(!this.html.length && this.bgimage.length){
31110                 cls += ' masonry-bottom-title';
31111             }
31112         }
31113         
31114         if(this.cls){
31115             cls += ' ' + this.cls;
31116         }
31117         
31118         var cfg = {
31119             tag: (this.href.length) ? 'a' : 'div',
31120             cls: cls,
31121             cn: [
31122                 {
31123                     tag: 'div',
31124                     cls: 'masonry-brick-paragraph',
31125                     cn: []
31126                 }
31127             ]
31128         };
31129         
31130         if(this.href.length){
31131             cfg.href = this.href;
31132         }
31133         
31134         var cn = cfg.cn[0].cn;
31135         
31136         if(this.title.length){
31137             cn.push({
31138                 tag: 'h4',
31139                 cls: 'masonry-brick-title',
31140                 html: this.title
31141             });
31142         }
31143         
31144         if(this.html.length){
31145             cn.push({
31146                 tag: 'p',
31147                 cls: 'masonry-brick-text',
31148                 html: this.html
31149             });
31150         }  
31151         if (!this.title.length && !this.html.length) {
31152             cfg.cn[0].cls += ' hide';
31153         }
31154         
31155         if(this.bgimage.length){
31156             cfg.cn.push({
31157                 tag: 'img',
31158                 cls: 'masonry-brick-image-view',
31159                 src: this.bgimage
31160             });
31161         }
31162         
31163         if(this.videourl.length){
31164             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31165             // youtube support only?
31166             cfg.cn.push({
31167                 tag: 'iframe',
31168                 cls: 'masonry-brick-image-view',
31169                 src: vurl,
31170                 frameborder : 0,
31171                 allowfullscreen : true
31172             });
31173             
31174             
31175         }
31176         
31177         cfg.cn.push({
31178             tag: 'div',
31179             cls: 'masonry-brick-mask'
31180         });
31181         
31182         return cfg;
31183         
31184     },
31185     
31186     getSplitAutoCreate : function()
31187     {
31188         var cls = 'masonry-brick masonry-brick-split';
31189         
31190         if(this.href.length){
31191             cls += ' masonry-brick-link';
31192         }
31193         
31194         if(this.bgimage.length){
31195             cls += ' masonry-brick-image';
31196         }
31197         
31198         if(this.size){
31199             cls += ' masonry-' + this.size + '-brick';
31200         }
31201         
31202         switch (this.placetitle) {
31203             case 'center' :
31204                 cls += ' masonry-center-title';
31205                 break;
31206             case 'bottom' :
31207                 cls += ' masonry-bottom-title';
31208                 break;
31209             default:
31210                 if(!this.bgimage.length){
31211                     cls += ' masonry-center-title';
31212                 }
31213
31214                 if(this.bgimage.length){
31215                     cls += ' masonry-bottom-title';
31216                 }
31217                 break;
31218         }
31219         
31220         if(this.cls){
31221             cls += ' ' + this.cls;
31222         }
31223         
31224         var cfg = {
31225             tag: (this.href.length) ? 'a' : 'div',
31226             cls: cls,
31227             cn: [
31228                 {
31229                     tag: 'div',
31230                     cls: 'masonry-brick-split-head',
31231                     cn: [
31232                         {
31233                             tag: 'div',
31234                             cls: 'masonry-brick-paragraph',
31235                             cn: []
31236                         }
31237                     ]
31238                 },
31239                 {
31240                     tag: 'div',
31241                     cls: 'masonry-brick-split-body',
31242                     cn: []
31243                 }
31244             ]
31245         };
31246         
31247         if(this.href.length){
31248             cfg.href = this.href;
31249         }
31250         
31251         if(this.title.length){
31252             cfg.cn[0].cn[0].cn.push({
31253                 tag: 'h4',
31254                 cls: 'masonry-brick-title',
31255                 html: this.title
31256             });
31257         }
31258         
31259         if(this.html.length){
31260             cfg.cn[1].cn.push({
31261                 tag: 'p',
31262                 cls: 'masonry-brick-text',
31263                 html: this.html
31264             });
31265         }
31266
31267         if(this.bgimage.length){
31268             cfg.cn[0].cn.push({
31269                 tag: 'img',
31270                 cls: 'masonry-brick-image-view',
31271                 src: this.bgimage
31272             });
31273         }
31274         
31275         if(this.videourl.length){
31276             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31277             // youtube support only?
31278             cfg.cn[0].cn.cn.push({
31279                 tag: 'iframe',
31280                 cls: 'masonry-brick-image-view',
31281                 src: vurl,
31282                 frameborder : 0,
31283                 allowfullscreen : true
31284             });
31285         }
31286         
31287         return cfg;
31288     },
31289     
31290     initEvents: function() 
31291     {
31292         switch (this.size) {
31293             case 'xs' :
31294                 this.x = 1;
31295                 this.y = 1;
31296                 break;
31297             case 'sm' :
31298                 this.x = 2;
31299                 this.y = 2;
31300                 break;
31301             case 'md' :
31302             case 'md-left' :
31303             case 'md-right' :
31304                 this.x = 3;
31305                 this.y = 3;
31306                 break;
31307             case 'tall' :
31308                 this.x = 2;
31309                 this.y = 3;
31310                 break;
31311             case 'wide' :
31312                 this.x = 3;
31313                 this.y = 2;
31314                 break;
31315             case 'wide-thin' :
31316                 this.x = 3;
31317                 this.y = 1;
31318                 break;
31319                         
31320             default :
31321                 break;
31322         }
31323         
31324         if(Roo.isTouch){
31325             this.el.on('touchstart', this.onTouchStart, this);
31326             this.el.on('touchmove', this.onTouchMove, this);
31327             this.el.on('touchend', this.onTouchEnd, this);
31328             this.el.on('contextmenu', this.onContextMenu, this);
31329         } else {
31330             this.el.on('mouseenter'  ,this.enter, this);
31331             this.el.on('mouseleave', this.leave, this);
31332             this.el.on('click', this.onClick, this);
31333         }
31334         
31335         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
31336             this.parent().bricks.push(this);   
31337         }
31338         
31339     },
31340     
31341     onClick: function(e, el)
31342     {
31343         var time = this.endTimer - this.startTimer;
31344         
31345         if(Roo.isTouch){
31346             if(time > 1000){
31347                 e.preventDefault();
31348                 return;
31349             }
31350         }
31351         
31352         if(!this.preventDefault){
31353             return;
31354         }
31355         
31356         e.preventDefault();
31357         this.fireEvent('click', this);
31358     },
31359     
31360     enter: function(e, el)
31361     {
31362         e.preventDefault();
31363         
31364         if(!this.isFitContainer){
31365             return;
31366         }
31367         
31368         if(this.bgimage.length && this.html.length){
31369             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31370         }
31371     },
31372     
31373     leave: function(e, el)
31374     {
31375         e.preventDefault();
31376         
31377         if(!this.isFitContainer){
31378             return;
31379         }
31380         
31381         if(this.bgimage.length && this.html.length){
31382             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31383         }
31384     },
31385     
31386     onTouchStart: function(e, el)
31387     {
31388 //        e.preventDefault();
31389         
31390         this.touchmoved = false;
31391         
31392         if(!this.isFitContainer){
31393             return;
31394         }
31395         
31396         if(!this.bgimage.length || !this.html.length){
31397             return;
31398         }
31399         
31400         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31401         
31402         this.timer = new Date().getTime();
31403         
31404     },
31405     
31406     onTouchMove: function(e, el)
31407     {
31408         this.touchmoved = true;
31409     },
31410     
31411     onContextMenu : function(e,el)
31412     {
31413         e.preventDefault();
31414         e.stopPropagation();
31415         return false;
31416     },
31417     
31418     onTouchEnd: function(e, el)
31419     {
31420 //        e.preventDefault();
31421         
31422         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
31423         
31424             this.leave(e,el);
31425             
31426             return;
31427         }
31428         
31429         if(!this.bgimage.length || !this.html.length){
31430             
31431             if(this.href.length){
31432                 window.location.href = this.href;
31433             }
31434             
31435             return;
31436         }
31437         
31438         if(!this.isFitContainer){
31439             return;
31440         }
31441         
31442         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31443         
31444         window.location.href = this.href;
31445     }
31446     
31447 });
31448
31449  
31450
31451  /*
31452  * - LGPL
31453  *
31454  * element
31455  * 
31456  */
31457
31458 /**
31459  * @class Roo.bootstrap.Brick
31460  * @extends Roo.bootstrap.Component
31461  * Bootstrap Brick class
31462  * 
31463  * @constructor
31464  * Create a new Brick
31465  * @param {Object} config The config object
31466  */
31467
31468 Roo.bootstrap.Brick = function(config){
31469     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
31470     
31471     this.addEvents({
31472         // raw events
31473         /**
31474          * @event click
31475          * When a Brick is click
31476          * @param {Roo.bootstrap.Brick} this
31477          * @param {Roo.EventObject} e
31478          */
31479         "click" : true
31480     });
31481 };
31482
31483 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
31484     
31485     /**
31486      * @cfg {String} title
31487      */   
31488     title : '',
31489     /**
31490      * @cfg {String} html
31491      */   
31492     html : '',
31493     /**
31494      * @cfg {String} bgimage
31495      */   
31496     bgimage : '',
31497     /**
31498      * @cfg {String} cls
31499      */   
31500     cls : '',
31501     /**
31502      * @cfg {String} href
31503      */   
31504     href : '',
31505     /**
31506      * @cfg {String} video
31507      */   
31508     video : '',
31509     /**
31510      * @cfg {Boolean} square
31511      */   
31512     square : true,
31513     
31514     getAutoCreate : function()
31515     {
31516         var cls = 'roo-brick';
31517         
31518         if(this.href.length){
31519             cls += ' roo-brick-link';
31520         }
31521         
31522         if(this.bgimage.length){
31523             cls += ' roo-brick-image';
31524         }
31525         
31526         if(!this.html.length && !this.bgimage.length){
31527             cls += ' roo-brick-center-title';
31528         }
31529         
31530         if(!this.html.length && this.bgimage.length){
31531             cls += ' roo-brick-bottom-title';
31532         }
31533         
31534         if(this.cls){
31535             cls += ' ' + this.cls;
31536         }
31537         
31538         var cfg = {
31539             tag: (this.href.length) ? 'a' : 'div',
31540             cls: cls,
31541             cn: [
31542                 {
31543                     tag: 'div',
31544                     cls: 'roo-brick-paragraph',
31545                     cn: []
31546                 }
31547             ]
31548         };
31549         
31550         if(this.href.length){
31551             cfg.href = this.href;
31552         }
31553         
31554         var cn = cfg.cn[0].cn;
31555         
31556         if(this.title.length){
31557             cn.push({
31558                 tag: 'h4',
31559                 cls: 'roo-brick-title',
31560                 html: this.title
31561             });
31562         }
31563         
31564         if(this.html.length){
31565             cn.push({
31566                 tag: 'p',
31567                 cls: 'roo-brick-text',
31568                 html: this.html
31569             });
31570         } else {
31571             cn.cls += ' hide';
31572         }
31573         
31574         if(this.bgimage.length){
31575             cfg.cn.push({
31576                 tag: 'img',
31577                 cls: 'roo-brick-image-view',
31578                 src: this.bgimage
31579             });
31580         }
31581         
31582         return cfg;
31583     },
31584     
31585     initEvents: function() 
31586     {
31587         if(this.title.length || this.html.length){
31588             this.el.on('mouseenter'  ,this.enter, this);
31589             this.el.on('mouseleave', this.leave, this);
31590         }
31591         
31592         
31593         Roo.EventManager.onWindowResize(this.resize, this); 
31594         
31595         this.resize();
31596     },
31597     
31598     resize : function()
31599     {
31600         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
31601         
31602         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
31603         
31604         if(this.bgimage.length){
31605             var image = this.el.select('.roo-brick-image-view', true).first();
31606             image.setWidth(paragraph.getWidth());
31607             image.setHeight(paragraph.getWidth());
31608             
31609             this.el.setHeight(paragraph.getWidth());
31610             
31611         }
31612         
31613     },
31614     
31615     enter: function(e, el)
31616     {
31617         e.preventDefault();
31618         
31619         if(this.bgimage.length){
31620             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
31621             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
31622         }
31623     },
31624     
31625     leave: function(e, el)
31626     {
31627         e.preventDefault();
31628         
31629         if(this.bgimage.length){
31630             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
31631             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
31632         }
31633     }
31634     
31635 });
31636
31637  
31638
31639  /*
31640  * - LGPL
31641  *
31642  * Input
31643  * 
31644  */
31645
31646 /**
31647  * @class Roo.bootstrap.NumberField
31648  * @extends Roo.bootstrap.Input
31649  * Bootstrap NumberField class
31650  * 
31651  * 
31652  * 
31653  * 
31654  * @constructor
31655  * Create a new NumberField
31656  * @param {Object} config The config object
31657  */
31658
31659 Roo.bootstrap.NumberField = function(config){
31660     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
31661 };
31662
31663 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
31664     
31665     /**
31666      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
31667      */
31668     allowDecimals : true,
31669     /**
31670      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
31671      */
31672     decimalSeparator : ".",
31673     /**
31674      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
31675      */
31676     decimalPrecision : 2,
31677     /**
31678      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
31679      */
31680     allowNegative : true,
31681     /**
31682      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
31683      */
31684     minValue : Number.NEGATIVE_INFINITY,
31685     /**
31686      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
31687      */
31688     maxValue : Number.MAX_VALUE,
31689     /**
31690      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
31691      */
31692     minText : "The minimum value for this field is {0}",
31693     /**
31694      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
31695      */
31696     maxText : "The maximum value for this field is {0}",
31697     /**
31698      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
31699      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
31700      */
31701     nanText : "{0} is not a valid number",
31702     /**
31703      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
31704      */
31705     castInt : true,
31706
31707     // private
31708     initEvents : function()
31709     {   
31710         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
31711         
31712         var allowed = "0123456789";
31713         
31714         if(this.allowDecimals){
31715             allowed += this.decimalSeparator;
31716         }
31717         
31718         if(this.allowNegative){
31719             allowed += "-";
31720         }
31721         
31722         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
31723         
31724         var keyPress = function(e){
31725             
31726             var k = e.getKey();
31727             
31728             var c = e.getCharCode();
31729             
31730             if(
31731                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
31732                     allowed.indexOf(String.fromCharCode(c)) === -1
31733             ){
31734                 e.stopEvent();
31735                 return;
31736             }
31737             
31738             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
31739                 return;
31740             }
31741             
31742             if(allowed.indexOf(String.fromCharCode(c)) === -1){
31743                 e.stopEvent();
31744             }
31745         };
31746         
31747         this.el.on("keypress", keyPress, this);
31748     },
31749     
31750     validateValue : function(value)
31751     {
31752         
31753         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
31754             return false;
31755         }
31756         
31757         var num = this.parseValue(value);
31758         
31759         if(isNaN(num)){
31760             this.markInvalid(String.format(this.nanText, value));
31761             return false;
31762         }
31763         
31764         if(num < this.minValue){
31765             this.markInvalid(String.format(this.minText, this.minValue));
31766             return false;
31767         }
31768         
31769         if(num > this.maxValue){
31770             this.markInvalid(String.format(this.maxText, this.maxValue));
31771             return false;
31772         }
31773         
31774         return true;
31775     },
31776
31777     getValue : function()
31778     {
31779         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
31780     },
31781
31782     parseValue : function(value)
31783     {
31784         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
31785         return isNaN(value) ? '' : value;
31786     },
31787
31788     fixPrecision : function(value)
31789     {
31790         var nan = isNaN(value);
31791         
31792         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
31793             return nan ? '' : value;
31794         }
31795         return parseFloat(value).toFixed(this.decimalPrecision);
31796     },
31797
31798     setValue : function(v)
31799     {
31800         v = this.fixPrecision(v);
31801         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
31802     },
31803
31804     decimalPrecisionFcn : function(v)
31805     {
31806         return Math.floor(v);
31807     },
31808
31809     beforeBlur : function()
31810     {
31811         if(!this.castInt){
31812             return;
31813         }
31814         
31815         var v = this.parseValue(this.getRawValue());
31816         if(v){
31817             this.setValue(v);
31818         }
31819     }
31820     
31821 });
31822
31823  
31824
31825 /*
31826 * Licence: LGPL
31827 */
31828
31829 /**
31830  * @class Roo.bootstrap.DocumentSlider
31831  * @extends Roo.bootstrap.Component
31832  * Bootstrap DocumentSlider class
31833  * 
31834  * @constructor
31835  * Create a new DocumentViewer
31836  * @param {Object} config The config object
31837  */
31838
31839 Roo.bootstrap.DocumentSlider = function(config){
31840     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
31841     
31842     this.addEvents({
31843         /**
31844          * @event initial
31845          * Fire after initEvent
31846          * @param {Roo.bootstrap.DocumentViewer} this
31847          */
31848         "initial" : true
31849     });
31850 };
31851
31852 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
31853     
31854     getAutoCreate : function()
31855     {
31856         var cfg = {
31857             tag : 'div',
31858             cls : 'roo-document-slider',
31859             cn : [
31860                 {
31861                     tag : 'div',
31862                     cls : 'roo-document-slider-body',
31863                     cn : [
31864                         {
31865                             tag : 'div',
31866                             cls : 'roo-document-slider-prev',
31867                             cn : [
31868                                 {
31869                                     tag : 'i',
31870                                     cls : 'fa fa-chevron-left'
31871                                 }
31872                             ]
31873                         },
31874                         {
31875                             tag : 'div',
31876                             cls : 'roo-document-slider-thumb',
31877                             cn : [
31878                                 {
31879                                     tag : 'img',
31880                                     cls : 'roo-document-slider-image'
31881                                 }
31882                             ]
31883                         },
31884                         {
31885                             tag : 'div',
31886                             cls : 'roo-document-slider-next',
31887                             cn : [
31888                                 {
31889                                     tag : 'i',
31890                                     cls : 'fa fa-chevron-right'
31891                                 }
31892                             ]
31893                         }
31894                     ]
31895                 }
31896             ]
31897         };
31898         
31899         return cfg;
31900     },
31901     
31902     initEvents : function()
31903     {
31904         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
31905         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31906         
31907         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
31908         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31909         
31910         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
31911         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31912     },
31913     
31914     initial : function()
31915     {
31916         this.fireEvent('initial', this);
31917     }
31918 });
31919 /*
31920  * Based on:
31921  * Ext JS Library 1.1.1
31922  * Copyright(c) 2006-2007, Ext JS, LLC.
31923  *
31924  * Originally Released Under LGPL - original licence link has changed is not relivant.
31925  *
31926  * Fork - LGPL
31927  * <script type="text/javascript">
31928  */
31929
31930
31931 /**
31932  * @class Roo.bootstrap.SplitBar
31933  * @extends Roo.util.Observable
31934  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
31935  * <br><br>
31936  * Usage:
31937  * <pre><code>
31938 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
31939                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
31940 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
31941 split.minSize = 100;
31942 split.maxSize = 600;
31943 split.animate = true;
31944 split.on('moved', splitterMoved);
31945 </code></pre>
31946  * @constructor
31947  * Create a new SplitBar
31948  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
31949  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
31950  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31951  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
31952                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
31953                         position of the SplitBar).
31954  */
31955 Roo.bootstrap.SplitBar = function(cfg){
31956     
31957     /** @private */
31958     
31959     //{
31960     //  dragElement : elm
31961     //  resizingElement: el,
31962         // optional..
31963     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
31964     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
31965         // existingProxy ???
31966     //}
31967     
31968     this.el = Roo.get(cfg.dragElement, true);
31969     this.el.dom.unselectable = "on";
31970     /** @private */
31971     this.resizingEl = Roo.get(cfg.resizingElement, true);
31972
31973     /**
31974      * @private
31975      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31976      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
31977      * @type Number
31978      */
31979     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
31980     
31981     /**
31982      * The minimum size of the resizing element. (Defaults to 0)
31983      * @type Number
31984      */
31985     this.minSize = 0;
31986     
31987     /**
31988      * The maximum size of the resizing element. (Defaults to 2000)
31989      * @type Number
31990      */
31991     this.maxSize = 2000;
31992     
31993     /**
31994      * Whether to animate the transition to the new size
31995      * @type Boolean
31996      */
31997     this.animate = false;
31998     
31999     /**
32000      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
32001      * @type Boolean
32002      */
32003     this.useShim = false;
32004     
32005     /** @private */
32006     this.shim = null;
32007     
32008     if(!cfg.existingProxy){
32009         /** @private */
32010         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
32011     }else{
32012         this.proxy = Roo.get(cfg.existingProxy).dom;
32013     }
32014     /** @private */
32015     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
32016     
32017     /** @private */
32018     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
32019     
32020     /** @private */
32021     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
32022     
32023     /** @private */
32024     this.dragSpecs = {};
32025     
32026     /**
32027      * @private The adapter to use to positon and resize elements
32028      */
32029     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32030     this.adapter.init(this);
32031     
32032     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32033         /** @private */
32034         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
32035         this.el.addClass("roo-splitbar-h");
32036     }else{
32037         /** @private */
32038         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
32039         this.el.addClass("roo-splitbar-v");
32040     }
32041     
32042     this.addEvents({
32043         /**
32044          * @event resize
32045          * Fires when the splitter is moved (alias for {@link #event-moved})
32046          * @param {Roo.bootstrap.SplitBar} this
32047          * @param {Number} newSize the new width or height
32048          */
32049         "resize" : true,
32050         /**
32051          * @event moved
32052          * Fires when the splitter is moved
32053          * @param {Roo.bootstrap.SplitBar} this
32054          * @param {Number} newSize the new width or height
32055          */
32056         "moved" : true,
32057         /**
32058          * @event beforeresize
32059          * Fires before the splitter is dragged
32060          * @param {Roo.bootstrap.SplitBar} this
32061          */
32062         "beforeresize" : true,
32063
32064         "beforeapply" : true
32065     });
32066
32067     Roo.util.Observable.call(this);
32068 };
32069
32070 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
32071     onStartProxyDrag : function(x, y){
32072         this.fireEvent("beforeresize", this);
32073         if(!this.overlay){
32074             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
32075             o.unselectable();
32076             o.enableDisplayMode("block");
32077             // all splitbars share the same overlay
32078             Roo.bootstrap.SplitBar.prototype.overlay = o;
32079         }
32080         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32081         this.overlay.show();
32082         Roo.get(this.proxy).setDisplayed("block");
32083         var size = this.adapter.getElementSize(this);
32084         this.activeMinSize = this.getMinimumSize();;
32085         this.activeMaxSize = this.getMaximumSize();;
32086         var c1 = size - this.activeMinSize;
32087         var c2 = Math.max(this.activeMaxSize - size, 0);
32088         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32089             this.dd.resetConstraints();
32090             this.dd.setXConstraint(
32091                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
32092                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
32093             );
32094             this.dd.setYConstraint(0, 0);
32095         }else{
32096             this.dd.resetConstraints();
32097             this.dd.setXConstraint(0, 0);
32098             this.dd.setYConstraint(
32099                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
32100                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
32101             );
32102          }
32103         this.dragSpecs.startSize = size;
32104         this.dragSpecs.startPoint = [x, y];
32105         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
32106     },
32107     
32108     /** 
32109      * @private Called after the drag operation by the DDProxy
32110      */
32111     onEndProxyDrag : function(e){
32112         Roo.get(this.proxy).setDisplayed(false);
32113         var endPoint = Roo.lib.Event.getXY(e);
32114         if(this.overlay){
32115             this.overlay.hide();
32116         }
32117         var newSize;
32118         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32119             newSize = this.dragSpecs.startSize + 
32120                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
32121                     endPoint[0] - this.dragSpecs.startPoint[0] :
32122                     this.dragSpecs.startPoint[0] - endPoint[0]
32123                 );
32124         }else{
32125             newSize = this.dragSpecs.startSize + 
32126                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
32127                     endPoint[1] - this.dragSpecs.startPoint[1] :
32128                     this.dragSpecs.startPoint[1] - endPoint[1]
32129                 );
32130         }
32131         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
32132         if(newSize != this.dragSpecs.startSize){
32133             if(this.fireEvent('beforeapply', this, newSize) !== false){
32134                 this.adapter.setElementSize(this, newSize);
32135                 this.fireEvent("moved", this, newSize);
32136                 this.fireEvent("resize", this, newSize);
32137             }
32138         }
32139     },
32140     
32141     /**
32142      * Get the adapter this SplitBar uses
32143      * @return The adapter object
32144      */
32145     getAdapter : function(){
32146         return this.adapter;
32147     },
32148     
32149     /**
32150      * Set the adapter this SplitBar uses
32151      * @param {Object} adapter A SplitBar adapter object
32152      */
32153     setAdapter : function(adapter){
32154         this.adapter = adapter;
32155         this.adapter.init(this);
32156     },
32157     
32158     /**
32159      * Gets the minimum size for the resizing element
32160      * @return {Number} The minimum size
32161      */
32162     getMinimumSize : function(){
32163         return this.minSize;
32164     },
32165     
32166     /**
32167      * Sets the minimum size for the resizing element
32168      * @param {Number} minSize The minimum size
32169      */
32170     setMinimumSize : function(minSize){
32171         this.minSize = minSize;
32172     },
32173     
32174     /**
32175      * Gets the maximum size for the resizing element
32176      * @return {Number} The maximum size
32177      */
32178     getMaximumSize : function(){
32179         return this.maxSize;
32180     },
32181     
32182     /**
32183      * Sets the maximum size for the resizing element
32184      * @param {Number} maxSize The maximum size
32185      */
32186     setMaximumSize : function(maxSize){
32187         this.maxSize = maxSize;
32188     },
32189     
32190     /**
32191      * Sets the initialize size for the resizing element
32192      * @param {Number} size The initial size
32193      */
32194     setCurrentSize : function(size){
32195         var oldAnimate = this.animate;
32196         this.animate = false;
32197         this.adapter.setElementSize(this, size);
32198         this.animate = oldAnimate;
32199     },
32200     
32201     /**
32202      * Destroy this splitbar. 
32203      * @param {Boolean} removeEl True to remove the element
32204      */
32205     destroy : function(removeEl){
32206         if(this.shim){
32207             this.shim.remove();
32208         }
32209         this.dd.unreg();
32210         this.proxy.parentNode.removeChild(this.proxy);
32211         if(removeEl){
32212             this.el.remove();
32213         }
32214     }
32215 });
32216
32217 /**
32218  * @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.
32219  */
32220 Roo.bootstrap.SplitBar.createProxy = function(dir){
32221     var proxy = new Roo.Element(document.createElement("div"));
32222     proxy.unselectable();
32223     var cls = 'roo-splitbar-proxy';
32224     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
32225     document.body.appendChild(proxy.dom);
32226     return proxy.dom;
32227 };
32228
32229 /** 
32230  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
32231  * Default Adapter. It assumes the splitter and resizing element are not positioned
32232  * elements and only gets/sets the width of the element. Generally used for table based layouts.
32233  */
32234 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
32235 };
32236
32237 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
32238     // do nothing for now
32239     init : function(s){
32240     
32241     },
32242     /**
32243      * Called before drag operations to get the current size of the resizing element. 
32244      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32245      */
32246      getElementSize : function(s){
32247         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32248             return s.resizingEl.getWidth();
32249         }else{
32250             return s.resizingEl.getHeight();
32251         }
32252     },
32253     
32254     /**
32255      * Called after drag operations to set the size of the resizing element.
32256      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32257      * @param {Number} newSize The new size to set
32258      * @param {Function} onComplete A function to be invoked when resizing is complete
32259      */
32260     setElementSize : function(s, newSize, onComplete){
32261         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32262             if(!s.animate){
32263                 s.resizingEl.setWidth(newSize);
32264                 if(onComplete){
32265                     onComplete(s, newSize);
32266                 }
32267             }else{
32268                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
32269             }
32270         }else{
32271             
32272             if(!s.animate){
32273                 s.resizingEl.setHeight(newSize);
32274                 if(onComplete){
32275                     onComplete(s, newSize);
32276                 }
32277             }else{
32278                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
32279             }
32280         }
32281     }
32282 };
32283
32284 /** 
32285  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
32286  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
32287  * Adapter that  moves the splitter element to align with the resized sizing element. 
32288  * Used with an absolute positioned SplitBar.
32289  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
32290  * document.body, make sure you assign an id to the body element.
32291  */
32292 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
32293     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32294     this.container = Roo.get(container);
32295 };
32296
32297 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
32298     init : function(s){
32299         this.basic.init(s);
32300     },
32301     
32302     getElementSize : function(s){
32303         return this.basic.getElementSize(s);
32304     },
32305     
32306     setElementSize : function(s, newSize, onComplete){
32307         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
32308     },
32309     
32310     moveSplitter : function(s){
32311         var yes = Roo.bootstrap.SplitBar;
32312         switch(s.placement){
32313             case yes.LEFT:
32314                 s.el.setX(s.resizingEl.getRight());
32315                 break;
32316             case yes.RIGHT:
32317                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
32318                 break;
32319             case yes.TOP:
32320                 s.el.setY(s.resizingEl.getBottom());
32321                 break;
32322             case yes.BOTTOM:
32323                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
32324                 break;
32325         }
32326     }
32327 };
32328
32329 /**
32330  * Orientation constant - Create a vertical SplitBar
32331  * @static
32332  * @type Number
32333  */
32334 Roo.bootstrap.SplitBar.VERTICAL = 1;
32335
32336 /**
32337  * Orientation constant - Create a horizontal SplitBar
32338  * @static
32339  * @type Number
32340  */
32341 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
32342
32343 /**
32344  * Placement constant - The resizing element is to the left of the splitter element
32345  * @static
32346  * @type Number
32347  */
32348 Roo.bootstrap.SplitBar.LEFT = 1;
32349
32350 /**
32351  * Placement constant - The resizing element is to the right of the splitter element
32352  * @static
32353  * @type Number
32354  */
32355 Roo.bootstrap.SplitBar.RIGHT = 2;
32356
32357 /**
32358  * Placement constant - The resizing element is positioned above the splitter element
32359  * @static
32360  * @type Number
32361  */
32362 Roo.bootstrap.SplitBar.TOP = 3;
32363
32364 /**
32365  * Placement constant - The resizing element is positioned under splitter element
32366  * @static
32367  * @type Number
32368  */
32369 Roo.bootstrap.SplitBar.BOTTOM = 4;
32370 Roo.namespace("Roo.bootstrap.layout");/*
32371  * Based on:
32372  * Ext JS Library 1.1.1
32373  * Copyright(c) 2006-2007, Ext JS, LLC.
32374  *
32375  * Originally Released Under LGPL - original licence link has changed is not relivant.
32376  *
32377  * Fork - LGPL
32378  * <script type="text/javascript">
32379  */
32380
32381 /**
32382  * @class Roo.bootstrap.layout.Manager
32383  * @extends Roo.bootstrap.Component
32384  * Base class for layout managers.
32385  */
32386 Roo.bootstrap.layout.Manager = function(config)
32387 {
32388     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
32389
32390
32391
32392
32393
32394     /** false to disable window resize monitoring @type Boolean */
32395     this.monitorWindowResize = true;
32396     this.regions = {};
32397     this.addEvents({
32398         /**
32399          * @event layout
32400          * Fires when a layout is performed.
32401          * @param {Roo.LayoutManager} this
32402          */
32403         "layout" : true,
32404         /**
32405          * @event regionresized
32406          * Fires when the user resizes a region.
32407          * @param {Roo.LayoutRegion} region The resized region
32408          * @param {Number} newSize The new size (width for east/west, height for north/south)
32409          */
32410         "regionresized" : true,
32411         /**
32412          * @event regioncollapsed
32413          * Fires when a region is collapsed.
32414          * @param {Roo.LayoutRegion} region The collapsed region
32415          */
32416         "regioncollapsed" : true,
32417         /**
32418          * @event regionexpanded
32419          * Fires when a region is expanded.
32420          * @param {Roo.LayoutRegion} region The expanded region
32421          */
32422         "regionexpanded" : true
32423     });
32424     this.updating = false;
32425
32426     if (config.el) {
32427         this.el = Roo.get(config.el);
32428         this.initEvents();
32429     }
32430
32431 };
32432
32433 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
32434
32435
32436     regions : null,
32437
32438     monitorWindowResize : true,
32439
32440
32441     updating : false,
32442
32443
32444     onRender : function(ct, position)
32445     {
32446         if(!this.el){
32447             this.el = Roo.get(ct);
32448             this.initEvents();
32449         }
32450         //this.fireEvent('render',this);
32451     },
32452
32453
32454     initEvents: function()
32455     {
32456
32457
32458         // ie scrollbar fix
32459         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32460             document.body.scroll = "no";
32461         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32462             this.el.position('relative');
32463         }
32464         this.id = this.el.id;
32465         this.el.addClass("roo-layout-container");
32466         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32467         if(this.el.dom != document.body ) {
32468             this.el.on('resize', this.layout,this);
32469             this.el.on('show', this.layout,this);
32470         }
32471
32472     },
32473
32474     /**
32475      * Returns true if this layout is currently being updated
32476      * @return {Boolean}
32477      */
32478     isUpdating : function(){
32479         return this.updating;
32480     },
32481
32482     /**
32483      * Suspend the LayoutManager from doing auto-layouts while
32484      * making multiple add or remove calls
32485      */
32486     beginUpdate : function(){
32487         this.updating = true;
32488     },
32489
32490     /**
32491      * Restore auto-layouts and optionally disable the manager from performing a layout
32492      * @param {Boolean} noLayout true to disable a layout update
32493      */
32494     endUpdate : function(noLayout){
32495         this.updating = false;
32496         if(!noLayout){
32497             this.layout();
32498         }
32499     },
32500
32501     layout: function(){
32502         // abstract...
32503     },
32504
32505     onRegionResized : function(region, newSize){
32506         this.fireEvent("regionresized", region, newSize);
32507         this.layout();
32508     },
32509
32510     onRegionCollapsed : function(region){
32511         this.fireEvent("regioncollapsed", region);
32512     },
32513
32514     onRegionExpanded : function(region){
32515         this.fireEvent("regionexpanded", region);
32516     },
32517
32518     /**
32519      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32520      * performs box-model adjustments.
32521      * @return {Object} The size as an object {width: (the width), height: (the height)}
32522      */
32523     getViewSize : function()
32524     {
32525         var size;
32526         if(this.el.dom != document.body){
32527             size = this.el.getSize();
32528         }else{
32529             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32530         }
32531         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32532         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32533         return size;
32534     },
32535
32536     /**
32537      * Returns the Element this layout is bound to.
32538      * @return {Roo.Element}
32539      */
32540     getEl : function(){
32541         return this.el;
32542     },
32543
32544     /**
32545      * Returns the specified region.
32546      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32547      * @return {Roo.LayoutRegion}
32548      */
32549     getRegion : function(target){
32550         return this.regions[target.toLowerCase()];
32551     },
32552
32553     onWindowResize : function(){
32554         if(this.monitorWindowResize){
32555             this.layout();
32556         }
32557     }
32558 });
32559 /*
32560  * Based on:
32561  * Ext JS Library 1.1.1
32562  * Copyright(c) 2006-2007, Ext JS, LLC.
32563  *
32564  * Originally Released Under LGPL - original licence link has changed is not relivant.
32565  *
32566  * Fork - LGPL
32567  * <script type="text/javascript">
32568  */
32569 /**
32570  * @class Roo.bootstrap.layout.Border
32571  * @extends Roo.bootstrap.layout.Manager
32572  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32573  * please see: examples/bootstrap/nested.html<br><br>
32574  
32575 <b>The container the layout is rendered into can be either the body element or any other element.
32576 If it is not the body element, the container needs to either be an absolute positioned element,
32577 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32578 the container size if it is not the body element.</b>
32579
32580 * @constructor
32581 * Create a new Border
32582 * @param {Object} config Configuration options
32583  */
32584 Roo.bootstrap.layout.Border = function(config){
32585     config = config || {};
32586     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
32587     
32588     
32589     
32590     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32591         if(config[region]){
32592             config[region].region = region;
32593             this.addRegion(config[region]);
32594         }
32595     },this);
32596     
32597 };
32598
32599 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
32600
32601 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
32602     /**
32603      * Creates and adds a new region if it doesn't already exist.
32604      * @param {String} target The target region key (north, south, east, west or center).
32605      * @param {Object} config The regions config object
32606      * @return {BorderLayoutRegion} The new region
32607      */
32608     addRegion : function(config)
32609     {
32610         if(!this.regions[config.region]){
32611             var r = this.factory(config);
32612             this.bindRegion(r);
32613         }
32614         return this.regions[config.region];
32615     },
32616
32617     // private (kinda)
32618     bindRegion : function(r){
32619         this.regions[r.config.region] = r;
32620         
32621         r.on("visibilitychange",    this.layout, this);
32622         r.on("paneladded",          this.layout, this);
32623         r.on("panelremoved",        this.layout, this);
32624         r.on("invalidated",         this.layout, this);
32625         r.on("resized",             this.onRegionResized, this);
32626         r.on("collapsed",           this.onRegionCollapsed, this);
32627         r.on("expanded",            this.onRegionExpanded, this);
32628     },
32629
32630     /**
32631      * Performs a layout update.
32632      */
32633     layout : function()
32634     {
32635         if(this.updating) {
32636             return;
32637         }
32638         
32639         // render all the rebions if they have not been done alreayd?
32640         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32641             if(this.regions[region] && !this.regions[region].bodyEl){
32642                 this.regions[region].onRender(this.el)
32643             }
32644         },this);
32645         
32646         var size = this.getViewSize();
32647         var w = size.width;
32648         var h = size.height;
32649         var centerW = w;
32650         var centerH = h;
32651         var centerY = 0;
32652         var centerX = 0;
32653         //var x = 0, y = 0;
32654
32655         var rs = this.regions;
32656         var north = rs["north"];
32657         var south = rs["south"]; 
32658         var west = rs["west"];
32659         var east = rs["east"];
32660         var center = rs["center"];
32661         //if(this.hideOnLayout){ // not supported anymore
32662             //c.el.setStyle("display", "none");
32663         //}
32664         if(north && north.isVisible()){
32665             var b = north.getBox();
32666             var m = north.getMargins();
32667             b.width = w - (m.left+m.right);
32668             b.x = m.left;
32669             b.y = m.top;
32670             centerY = b.height + b.y + m.bottom;
32671             centerH -= centerY;
32672             north.updateBox(this.safeBox(b));
32673         }
32674         if(south && south.isVisible()){
32675             var b = south.getBox();
32676             var m = south.getMargins();
32677             b.width = w - (m.left+m.right);
32678             b.x = m.left;
32679             var totalHeight = (b.height + m.top + m.bottom);
32680             b.y = h - totalHeight + m.top;
32681             centerH -= totalHeight;
32682             south.updateBox(this.safeBox(b));
32683         }
32684         if(west && west.isVisible()){
32685             var b = west.getBox();
32686             var m = west.getMargins();
32687             b.height = centerH - (m.top+m.bottom);
32688             b.x = m.left;
32689             b.y = centerY + m.top;
32690             var totalWidth = (b.width + m.left + m.right);
32691             centerX += totalWidth;
32692             centerW -= totalWidth;
32693             west.updateBox(this.safeBox(b));
32694         }
32695         if(east && east.isVisible()){
32696             var b = east.getBox();
32697             var m = east.getMargins();
32698             b.height = centerH - (m.top+m.bottom);
32699             var totalWidth = (b.width + m.left + m.right);
32700             b.x = w - totalWidth + m.left;
32701             b.y = centerY + m.top;
32702             centerW -= totalWidth;
32703             east.updateBox(this.safeBox(b));
32704         }
32705         if(center){
32706             var m = center.getMargins();
32707             var centerBox = {
32708                 x: centerX + m.left,
32709                 y: centerY + m.top,
32710                 width: centerW - (m.left+m.right),
32711                 height: centerH - (m.top+m.bottom)
32712             };
32713             //if(this.hideOnLayout){
32714                 //center.el.setStyle("display", "block");
32715             //}
32716             center.updateBox(this.safeBox(centerBox));
32717         }
32718         this.el.repaint();
32719         this.fireEvent("layout", this);
32720     },
32721
32722     // private
32723     safeBox : function(box){
32724         box.width = Math.max(0, box.width);
32725         box.height = Math.max(0, box.height);
32726         return box;
32727     },
32728
32729     /**
32730      * Adds a ContentPanel (or subclass) to this layout.
32731      * @param {String} target The target region key (north, south, east, west or center).
32732      * @param {Roo.ContentPanel} panel The panel to add
32733      * @return {Roo.ContentPanel} The added panel
32734      */
32735     add : function(target, panel){
32736          
32737         target = target.toLowerCase();
32738         return this.regions[target].add(panel);
32739     },
32740
32741     /**
32742      * Remove a ContentPanel (or subclass) to this layout.
32743      * @param {String} target The target region key (north, south, east, west or center).
32744      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32745      * @return {Roo.ContentPanel} The removed panel
32746      */
32747     remove : function(target, panel){
32748         target = target.toLowerCase();
32749         return this.regions[target].remove(panel);
32750     },
32751
32752     /**
32753      * Searches all regions for a panel with the specified id
32754      * @param {String} panelId
32755      * @return {Roo.ContentPanel} The panel or null if it wasn't found
32756      */
32757     findPanel : function(panelId){
32758         var rs = this.regions;
32759         for(var target in rs){
32760             if(typeof rs[target] != "function"){
32761                 var p = rs[target].getPanel(panelId);
32762                 if(p){
32763                     return p;
32764                 }
32765             }
32766         }
32767         return null;
32768     },
32769
32770     /**
32771      * Searches all regions for a panel with the specified id and activates (shows) it.
32772      * @param {String/ContentPanel} panelId The panels id or the panel itself
32773      * @return {Roo.ContentPanel} The shown panel or null
32774      */
32775     showPanel : function(panelId) {
32776       var rs = this.regions;
32777       for(var target in rs){
32778          var r = rs[target];
32779          if(typeof r != "function"){
32780             if(r.hasPanel(panelId)){
32781                return r.showPanel(panelId);
32782             }
32783          }
32784       }
32785       return null;
32786    },
32787
32788    /**
32789      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32790      * @param {Roo.state.Provider} provider (optional) An alternate state provider
32791      */
32792    /*
32793     restoreState : function(provider){
32794         if(!provider){
32795             provider = Roo.state.Manager;
32796         }
32797         var sm = new Roo.LayoutStateManager();
32798         sm.init(this, provider);
32799     },
32800 */
32801  
32802  
32803     /**
32804      * Adds a xtype elements to the layout.
32805      * <pre><code>
32806
32807 layout.addxtype({
32808        xtype : 'ContentPanel',
32809        region: 'west',
32810        items: [ .... ]
32811    }
32812 );
32813
32814 layout.addxtype({
32815         xtype : 'NestedLayoutPanel',
32816         region: 'west',
32817         layout: {
32818            center: { },
32819            west: { }   
32820         },
32821         items : [ ... list of content panels or nested layout panels.. ]
32822    }
32823 );
32824 </code></pre>
32825      * @param {Object} cfg Xtype definition of item to add.
32826      */
32827     addxtype : function(cfg)
32828     {
32829         // basically accepts a pannel...
32830         // can accept a layout region..!?!?
32831         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32832         
32833         
32834         // theory?  children can only be panels??
32835         
32836         //if (!cfg.xtype.match(/Panel$/)) {
32837         //    return false;
32838         //}
32839         var ret = false;
32840         
32841         if (typeof(cfg.region) == 'undefined') {
32842             Roo.log("Failed to add Panel, region was not set");
32843             Roo.log(cfg);
32844             return false;
32845         }
32846         var region = cfg.region;
32847         delete cfg.region;
32848         
32849           
32850         var xitems = [];
32851         if (cfg.items) {
32852             xitems = cfg.items;
32853             delete cfg.items;
32854         }
32855         var nb = false;
32856         
32857         switch(cfg.xtype) 
32858         {
32859             case 'Content':  // ContentPanel (el, cfg)
32860             case 'Scroll':  // ContentPanel (el, cfg)
32861             case 'View': 
32862                 cfg.autoCreate = true;
32863                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32864                 //} else {
32865                 //    var el = this.el.createChild();
32866                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32867                 //}
32868                 
32869                 this.add(region, ret);
32870                 break;
32871             
32872             /*
32873             case 'TreePanel': // our new panel!
32874                 cfg.el = this.el.createChild();
32875                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32876                 this.add(region, ret);
32877                 break;
32878             */
32879             
32880             case 'Nest': 
32881                 // create a new Layout (which is  a Border Layout...
32882                 
32883                 var clayout = cfg.layout;
32884                 clayout.el  = this.el.createChild();
32885                 clayout.items   = clayout.items  || [];
32886                 
32887                 delete cfg.layout;
32888                 
32889                 // replace this exitems with the clayout ones..
32890                 xitems = clayout.items;
32891                  
32892                 // force background off if it's in center...
32893                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32894                     cfg.background = false;
32895                 }
32896                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
32897                 
32898                 
32899                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32900                 //console.log('adding nested layout panel '  + cfg.toSource());
32901                 this.add(region, ret);
32902                 nb = {}; /// find first...
32903                 break;
32904             
32905             case 'Grid':
32906                 
32907                 // needs grid and region
32908                 
32909                 //var el = this.getRegion(region).el.createChild();
32910                 /*
32911                  *var el = this.el.createChild();
32912                 // create the grid first...
32913                 cfg.grid.container = el;
32914                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
32915                 */
32916                 
32917                 if (region == 'center' && this.active ) {
32918                     cfg.background = false;
32919                 }
32920                 
32921                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32922                 
32923                 this.add(region, ret);
32924                 /*
32925                 if (cfg.background) {
32926                     // render grid on panel activation (if panel background)
32927                     ret.on('activate', function(gp) {
32928                         if (!gp.grid.rendered) {
32929                     //        gp.grid.render(el);
32930                         }
32931                     });
32932                 } else {
32933                   //  cfg.grid.render(el);
32934                 }
32935                 */
32936                 break;
32937            
32938            
32939             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
32940                 // it was the old xcomponent building that caused this before.
32941                 // espeically if border is the top element in the tree.
32942                 ret = this;
32943                 break; 
32944                 
32945                     
32946                 
32947                 
32948                 
32949             default:
32950                 /*
32951                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
32952                     
32953                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32954                     this.add(region, ret);
32955                 } else {
32956                 */
32957                     Roo.log(cfg);
32958                     throw "Can not add '" + cfg.xtype + "' to Border";
32959                     return null;
32960              
32961                                 
32962              
32963         }
32964         this.beginUpdate();
32965         // add children..
32966         var region = '';
32967         var abn = {};
32968         Roo.each(xitems, function(i)  {
32969             region = nb && i.region ? i.region : false;
32970             
32971             var add = ret.addxtype(i);
32972            
32973             if (region) {
32974                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32975                 if (!i.background) {
32976                     abn[region] = nb[region] ;
32977                 }
32978             }
32979             
32980         });
32981         this.endUpdate();
32982
32983         // make the last non-background panel active..
32984         //if (nb) { Roo.log(abn); }
32985         if (nb) {
32986             
32987             for(var r in abn) {
32988                 region = this.getRegion(r);
32989                 if (region) {
32990                     // tried using nb[r], but it does not work..
32991                      
32992                     region.showPanel(abn[r]);
32993                    
32994                 }
32995             }
32996         }
32997         return ret;
32998         
32999     },
33000     
33001     
33002 // private
33003     factory : function(cfg)
33004     {
33005         
33006         var validRegions = Roo.bootstrap.layout.Border.regions;
33007
33008         var target = cfg.region;
33009         cfg.mgr = this;
33010         
33011         var r = Roo.bootstrap.layout;
33012         Roo.log(target);
33013         switch(target){
33014             case "north":
33015                 return new r.North(cfg);
33016             case "south":
33017                 return new r.South(cfg);
33018             case "east":
33019                 return new r.East(cfg);
33020             case "west":
33021                 return new r.West(cfg);
33022             case "center":
33023                 return new r.Center(cfg);
33024         }
33025         throw 'Layout region "'+target+'" not supported.';
33026     }
33027     
33028     
33029 });
33030  /*
33031  * Based on:
33032  * Ext JS Library 1.1.1
33033  * Copyright(c) 2006-2007, Ext JS, LLC.
33034  *
33035  * Originally Released Under LGPL - original licence link has changed is not relivant.
33036  *
33037  * Fork - LGPL
33038  * <script type="text/javascript">
33039  */
33040  
33041 /**
33042  * @class Roo.bootstrap.layout.Basic
33043  * @extends Roo.util.Observable
33044  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33045  * and does not have a titlebar, tabs or any other features. All it does is size and position 
33046  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33047  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
33048  * @cfg {string}   region  the region that it inhabits..
33049  * @cfg {bool}   skipConfig skip config?
33050  * 
33051
33052  */
33053 Roo.bootstrap.layout.Basic = function(config){
33054     
33055     this.mgr = config.mgr;
33056     
33057     this.position = config.region;
33058     
33059     var skipConfig = config.skipConfig;
33060     
33061     this.events = {
33062         /**
33063          * @scope Roo.BasicLayoutRegion
33064          */
33065         
33066         /**
33067          * @event beforeremove
33068          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33069          * @param {Roo.LayoutRegion} this
33070          * @param {Roo.ContentPanel} panel The panel
33071          * @param {Object} e The cancel event object
33072          */
33073         "beforeremove" : true,
33074         /**
33075          * @event invalidated
33076          * Fires when the layout for this region is changed.
33077          * @param {Roo.LayoutRegion} this
33078          */
33079         "invalidated" : true,
33080         /**
33081          * @event visibilitychange
33082          * Fires when this region is shown or hidden 
33083          * @param {Roo.LayoutRegion} this
33084          * @param {Boolean} visibility true or false
33085          */
33086         "visibilitychange" : true,
33087         /**
33088          * @event paneladded
33089          * Fires when a panel is added. 
33090          * @param {Roo.LayoutRegion} this
33091          * @param {Roo.ContentPanel} panel The panel
33092          */
33093         "paneladded" : true,
33094         /**
33095          * @event panelremoved
33096          * Fires when a panel is removed. 
33097          * @param {Roo.LayoutRegion} this
33098          * @param {Roo.ContentPanel} panel The panel
33099          */
33100         "panelremoved" : true,
33101         /**
33102          * @event beforecollapse
33103          * Fires when this region before collapse.
33104          * @param {Roo.LayoutRegion} this
33105          */
33106         "beforecollapse" : true,
33107         /**
33108          * @event collapsed
33109          * Fires when this region is collapsed.
33110          * @param {Roo.LayoutRegion} this
33111          */
33112         "collapsed" : true,
33113         /**
33114          * @event expanded
33115          * Fires when this region is expanded.
33116          * @param {Roo.LayoutRegion} this
33117          */
33118         "expanded" : true,
33119         /**
33120          * @event slideshow
33121          * Fires when this region is slid into view.
33122          * @param {Roo.LayoutRegion} this
33123          */
33124         "slideshow" : true,
33125         /**
33126          * @event slidehide
33127          * Fires when this region slides out of view. 
33128          * @param {Roo.LayoutRegion} this
33129          */
33130         "slidehide" : true,
33131         /**
33132          * @event panelactivated
33133          * Fires when a panel is activated. 
33134          * @param {Roo.LayoutRegion} this
33135          * @param {Roo.ContentPanel} panel The activated panel
33136          */
33137         "panelactivated" : true,
33138         /**
33139          * @event resized
33140          * Fires when the user resizes this region. 
33141          * @param {Roo.LayoutRegion} this
33142          * @param {Number} newSize The new size (width for east/west, height for north/south)
33143          */
33144         "resized" : true
33145     };
33146     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33147     this.panels = new Roo.util.MixedCollection();
33148     this.panels.getKey = this.getPanelId.createDelegate(this);
33149     this.box = null;
33150     this.activePanel = null;
33151     // ensure listeners are added...
33152     
33153     if (config.listeners || config.events) {
33154         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
33155             listeners : config.listeners || {},
33156             events : config.events || {}
33157         });
33158     }
33159     
33160     if(skipConfig !== true){
33161         this.applyConfig(config);
33162     }
33163 };
33164
33165 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
33166 {
33167     getPanelId : function(p){
33168         return p.getId();
33169     },
33170     
33171     applyConfig : function(config){
33172         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33173         this.config = config;
33174         
33175     },
33176     
33177     /**
33178      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33179      * the width, for horizontal (north, south) the height.
33180      * @param {Number} newSize The new width or height
33181      */
33182     resizeTo : function(newSize){
33183         var el = this.el ? this.el :
33184                  (this.activePanel ? this.activePanel.getEl() : null);
33185         if(el){
33186             switch(this.position){
33187                 case "east":
33188                 case "west":
33189                     el.setWidth(newSize);
33190                     this.fireEvent("resized", this, newSize);
33191                 break;
33192                 case "north":
33193                 case "south":
33194                     el.setHeight(newSize);
33195                     this.fireEvent("resized", this, newSize);
33196                 break;                
33197             }
33198         }
33199     },
33200     
33201     getBox : function(){
33202         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33203     },
33204     
33205     getMargins : function(){
33206         return this.margins;
33207     },
33208     
33209     updateBox : function(box){
33210         this.box = box;
33211         var el = this.activePanel.getEl();
33212         el.dom.style.left = box.x + "px";
33213         el.dom.style.top = box.y + "px";
33214         this.activePanel.setSize(box.width, box.height);
33215     },
33216     
33217     /**
33218      * Returns the container element for this region.
33219      * @return {Roo.Element}
33220      */
33221     getEl : function(){
33222         return this.activePanel;
33223     },
33224     
33225     /**
33226      * Returns true if this region is currently visible.
33227      * @return {Boolean}
33228      */
33229     isVisible : function(){
33230         return this.activePanel ? true : false;
33231     },
33232     
33233     setActivePanel : function(panel){
33234         panel = this.getPanel(panel);
33235         if(this.activePanel && this.activePanel != panel){
33236             this.activePanel.setActiveState(false);
33237             this.activePanel.getEl().setLeftTop(-10000,-10000);
33238         }
33239         this.activePanel = panel;
33240         panel.setActiveState(true);
33241         if(this.box){
33242             panel.setSize(this.box.width, this.box.height);
33243         }
33244         this.fireEvent("panelactivated", this, panel);
33245         this.fireEvent("invalidated");
33246     },
33247     
33248     /**
33249      * Show the specified panel.
33250      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33251      * @return {Roo.ContentPanel} The shown panel or null
33252      */
33253     showPanel : function(panel){
33254         panel = this.getPanel(panel);
33255         if(panel){
33256             this.setActivePanel(panel);
33257         }
33258         return panel;
33259     },
33260     
33261     /**
33262      * Get the active panel for this region.
33263      * @return {Roo.ContentPanel} The active panel or null
33264      */
33265     getActivePanel : function(){
33266         return this.activePanel;
33267     },
33268     
33269     /**
33270      * Add the passed ContentPanel(s)
33271      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33272      * @return {Roo.ContentPanel} The panel added (if only one was added)
33273      */
33274     add : function(panel){
33275         if(arguments.length > 1){
33276             for(var i = 0, len = arguments.length; i < len; i++) {
33277                 this.add(arguments[i]);
33278             }
33279             return null;
33280         }
33281         if(this.hasPanel(panel)){
33282             this.showPanel(panel);
33283             return panel;
33284         }
33285         var el = panel.getEl();
33286         if(el.dom.parentNode != this.mgr.el.dom){
33287             this.mgr.el.dom.appendChild(el.dom);
33288         }
33289         if(panel.setRegion){
33290             panel.setRegion(this);
33291         }
33292         this.panels.add(panel);
33293         el.setStyle("position", "absolute");
33294         if(!panel.background){
33295             this.setActivePanel(panel);
33296             if(this.config.initialSize && this.panels.getCount()==1){
33297                 this.resizeTo(this.config.initialSize);
33298             }
33299         }
33300         this.fireEvent("paneladded", this, panel);
33301         return panel;
33302     },
33303     
33304     /**
33305      * Returns true if the panel is in this region.
33306      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33307      * @return {Boolean}
33308      */
33309     hasPanel : function(panel){
33310         if(typeof panel == "object"){ // must be panel obj
33311             panel = panel.getId();
33312         }
33313         return this.getPanel(panel) ? true : false;
33314     },
33315     
33316     /**
33317      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33318      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33319      * @param {Boolean} preservePanel Overrides the config preservePanel option
33320      * @return {Roo.ContentPanel} The panel that was removed
33321      */
33322     remove : function(panel, preservePanel){
33323         panel = this.getPanel(panel);
33324         if(!panel){
33325             return null;
33326         }
33327         var e = {};
33328         this.fireEvent("beforeremove", this, panel, e);
33329         if(e.cancel === true){
33330             return null;
33331         }
33332         var panelId = panel.getId();
33333         this.panels.removeKey(panelId);
33334         return panel;
33335     },
33336     
33337     /**
33338      * Returns the panel specified or null if it's not in this region.
33339      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33340      * @return {Roo.ContentPanel}
33341      */
33342     getPanel : function(id){
33343         if(typeof id == "object"){ // must be panel obj
33344             return id;
33345         }
33346         return this.panels.get(id);
33347     },
33348     
33349     /**
33350      * Returns this regions position (north/south/east/west/center).
33351      * @return {String} 
33352      */
33353     getPosition: function(){
33354         return this.position;    
33355     }
33356 });/*
33357  * Based on:
33358  * Ext JS Library 1.1.1
33359  * Copyright(c) 2006-2007, Ext JS, LLC.
33360  *
33361  * Originally Released Under LGPL - original licence link has changed is not relivant.
33362  *
33363  * Fork - LGPL
33364  * <script type="text/javascript">
33365  */
33366  
33367 /**
33368  * @class Roo.bootstrap.layout.Region
33369  * @extends Roo.bootstrap.layout.Basic
33370  * This class represents a region in a layout manager.
33371  
33372  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33373  * @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})
33374  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
33375  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33376  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33377  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33378  * @cfg {String}    title           The title for the region (overrides panel titles)
33379  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33380  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33381  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33382  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33383  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33384  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33385  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33386  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33387  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33388  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
33389
33390  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33391  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33392  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33393  * @cfg {Number}    width           For East/West panels
33394  * @cfg {Number}    height          For North/South panels
33395  * @cfg {Boolean}   split           To show the splitter
33396  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33397  * 
33398  * @cfg {string}   cls             Extra CSS classes to add to region
33399  * 
33400  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
33401  * @cfg {string}   region  the region that it inhabits..
33402  *
33403
33404  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
33405  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
33406
33407  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
33408  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
33409  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
33410  */
33411 Roo.bootstrap.layout.Region = function(config)
33412 {
33413     this.applyConfig(config);
33414
33415     var mgr = config.mgr;
33416     var pos = config.region;
33417     config.skipConfig = true;
33418     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
33419     
33420     if (mgr.el) {
33421         this.onRender(mgr.el);   
33422     }
33423      
33424     this.visible = true;
33425     this.collapsed = false;
33426     this.unrendered_panels = [];
33427 };
33428
33429 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
33430
33431     position: '', // set by wrapper (eg. north/south etc..)
33432     unrendered_panels : null,  // unrendered panels.
33433     createBody : function(){
33434         /** This region's body element 
33435         * @type Roo.Element */
33436         this.bodyEl = this.el.createChild({
33437                 tag: "div",
33438                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
33439         });
33440     },
33441
33442     onRender: function(ctr, pos)
33443     {
33444         var dh = Roo.DomHelper;
33445         /** This region's container element 
33446         * @type Roo.Element */
33447         this.el = dh.append(ctr.dom, {
33448                 tag: "div",
33449                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
33450             }, true);
33451         /** This region's title element 
33452         * @type Roo.Element */
33453     
33454         this.titleEl = dh.append(this.el.dom,
33455             {
33456                     tag: "div",
33457                     unselectable: "on",
33458                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
33459                     children:[
33460                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33461                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
33462                     ]}, true);
33463         
33464         this.titleEl.enableDisplayMode();
33465         /** This region's title text element 
33466         * @type HTMLElement */
33467         this.titleTextEl = this.titleEl.dom.firstChild;
33468         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33469         /*
33470         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
33471         this.closeBtn.enableDisplayMode();
33472         this.closeBtn.on("click", this.closeClicked, this);
33473         this.closeBtn.hide();
33474     */
33475         this.createBody(this.config);
33476         if(this.config.hideWhenEmpty){
33477             this.hide();
33478             this.on("paneladded", this.validateVisibility, this);
33479             this.on("panelremoved", this.validateVisibility, this);
33480         }
33481         if(this.autoScroll){
33482             this.bodyEl.setStyle("overflow", "auto");
33483         }else{
33484             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
33485         }
33486         //if(c.titlebar !== false){
33487             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
33488                 this.titleEl.hide();
33489             }else{
33490                 this.titleEl.show();
33491                 if(this.config.title){
33492                     this.titleTextEl.innerHTML = this.config.title;
33493                 }
33494             }
33495         //}
33496         if(this.config.collapsed){
33497             this.collapse(true);
33498         }
33499         if(this.config.hidden){
33500             this.hide();
33501         }
33502         
33503         if (this.unrendered_panels && this.unrendered_panels.length) {
33504             for (var i =0;i< this.unrendered_panels.length; i++) {
33505                 this.add(this.unrendered_panels[i]);
33506             }
33507             this.unrendered_panels = null;
33508             
33509         }
33510         
33511     },
33512     
33513     applyConfig : function(c)
33514     {
33515         /*
33516          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
33517             var dh = Roo.DomHelper;
33518             if(c.titlebar !== false){
33519                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
33520                 this.collapseBtn.on("click", this.collapse, this);
33521                 this.collapseBtn.enableDisplayMode();
33522                 /*
33523                 if(c.showPin === true || this.showPin){
33524                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
33525                     this.stickBtn.enableDisplayMode();
33526                     this.stickBtn.on("click", this.expand, this);
33527                     this.stickBtn.hide();
33528                 }
33529                 
33530             }
33531             */
33532             /** This region's collapsed element
33533             * @type Roo.Element */
33534             /*
33535              *
33536             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33537                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33538             ]}, true);
33539             
33540             if(c.floatable !== false){
33541                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33542                this.collapsedEl.on("click", this.collapseClick, this);
33543             }
33544
33545             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33546                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33547                    id: "message", unselectable: "on", style:{"float":"left"}});
33548                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33549              }
33550             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33551             this.expandBtn.on("click", this.expand, this);
33552             
33553         }
33554         
33555         if(this.collapseBtn){
33556             this.collapseBtn.setVisible(c.collapsible == true);
33557         }
33558         
33559         this.cmargins = c.cmargins || this.cmargins ||
33560                          (this.position == "west" || this.position == "east" ?
33561                              {top: 0, left: 2, right:2, bottom: 0} :
33562                              {top: 2, left: 0, right:0, bottom: 2});
33563         */
33564         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33565         
33566         
33567         this.bottomTabs = c.tabPosition != "top";
33568         
33569         this.autoScroll = c.autoScroll || false;
33570         
33571         
33572        
33573         
33574         this.duration = c.duration || .30;
33575         this.slideDuration = c.slideDuration || .45;
33576         this.config = c;
33577        
33578     },
33579     /**
33580      * Returns true if this region is currently visible.
33581      * @return {Boolean}
33582      */
33583     isVisible : function(){
33584         return this.visible;
33585     },
33586
33587     /**
33588      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33589      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33590      */
33591     //setCollapsedTitle : function(title){
33592     //    title = title || "&#160;";
33593      //   if(this.collapsedTitleTextEl){
33594       //      this.collapsedTitleTextEl.innerHTML = title;
33595        // }
33596     //},
33597
33598     getBox : function(){
33599         var b;
33600       //  if(!this.collapsed){
33601             b = this.el.getBox(false, true);
33602        // }else{
33603           //  b = this.collapsedEl.getBox(false, true);
33604         //}
33605         return b;
33606     },
33607
33608     getMargins : function(){
33609         return this.margins;
33610         //return this.collapsed ? this.cmargins : this.margins;
33611     },
33612 /*
33613     highlight : function(){
33614         this.el.addClass("x-layout-panel-dragover");
33615     },
33616
33617     unhighlight : function(){
33618         this.el.removeClass("x-layout-panel-dragover");
33619     },
33620 */
33621     updateBox : function(box)
33622     {
33623         if (!this.bodyEl) {
33624             return; // not rendered yet..
33625         }
33626         
33627         this.box = box;
33628         if(!this.collapsed){
33629             this.el.dom.style.left = box.x + "px";
33630             this.el.dom.style.top = box.y + "px";
33631             this.updateBody(box.width, box.height);
33632         }else{
33633             this.collapsedEl.dom.style.left = box.x + "px";
33634             this.collapsedEl.dom.style.top = box.y + "px";
33635             this.collapsedEl.setSize(box.width, box.height);
33636         }
33637         if(this.tabs){
33638             this.tabs.autoSizeTabs();
33639         }
33640     },
33641
33642     updateBody : function(w, h)
33643     {
33644         if(w !== null){
33645             this.el.setWidth(w);
33646             w -= this.el.getBorderWidth("rl");
33647             if(this.config.adjustments){
33648                 w += this.config.adjustments[0];
33649             }
33650         }
33651         if(h !== null && h > 0){
33652             this.el.setHeight(h);
33653             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33654             h -= this.el.getBorderWidth("tb");
33655             if(this.config.adjustments){
33656                 h += this.config.adjustments[1];
33657             }
33658             this.bodyEl.setHeight(h);
33659             if(this.tabs){
33660                 h = this.tabs.syncHeight(h);
33661             }
33662         }
33663         if(this.panelSize){
33664             w = w !== null ? w : this.panelSize.width;
33665             h = h !== null ? h : this.panelSize.height;
33666         }
33667         if(this.activePanel){
33668             var el = this.activePanel.getEl();
33669             w = w !== null ? w : el.getWidth();
33670             h = h !== null ? h : el.getHeight();
33671             this.panelSize = {width: w, height: h};
33672             this.activePanel.setSize(w, h);
33673         }
33674         if(Roo.isIE && this.tabs){
33675             this.tabs.el.repaint();
33676         }
33677     },
33678
33679     /**
33680      * Returns the container element for this region.
33681      * @return {Roo.Element}
33682      */
33683     getEl : function(){
33684         return this.el;
33685     },
33686
33687     /**
33688      * Hides this region.
33689      */
33690     hide : function(){
33691         //if(!this.collapsed){
33692             this.el.dom.style.left = "-2000px";
33693             this.el.hide();
33694         //}else{
33695          //   this.collapsedEl.dom.style.left = "-2000px";
33696          //   this.collapsedEl.hide();
33697        // }
33698         this.visible = false;
33699         this.fireEvent("visibilitychange", this, false);
33700     },
33701
33702     /**
33703      * Shows this region if it was previously hidden.
33704      */
33705     show : function(){
33706         //if(!this.collapsed){
33707             this.el.show();
33708         //}else{
33709         //    this.collapsedEl.show();
33710        // }
33711         this.visible = true;
33712         this.fireEvent("visibilitychange", this, true);
33713     },
33714 /*
33715     closeClicked : function(){
33716         if(this.activePanel){
33717             this.remove(this.activePanel);
33718         }
33719     },
33720
33721     collapseClick : function(e){
33722         if(this.isSlid){
33723            e.stopPropagation();
33724            this.slideIn();
33725         }else{
33726            e.stopPropagation();
33727            this.slideOut();
33728         }
33729     },
33730 */
33731     /**
33732      * Collapses this region.
33733      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33734      */
33735     /*
33736     collapse : function(skipAnim, skipCheck = false){
33737         if(this.collapsed) {
33738             return;
33739         }
33740         
33741         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
33742             
33743             this.collapsed = true;
33744             if(this.split){
33745                 this.split.el.hide();
33746             }
33747             if(this.config.animate && skipAnim !== true){
33748                 this.fireEvent("invalidated", this);
33749                 this.animateCollapse();
33750             }else{
33751                 this.el.setLocation(-20000,-20000);
33752                 this.el.hide();
33753                 this.collapsedEl.show();
33754                 this.fireEvent("collapsed", this);
33755                 this.fireEvent("invalidated", this);
33756             }
33757         }
33758         
33759     },
33760 */
33761     animateCollapse : function(){
33762         // overridden
33763     },
33764
33765     /**
33766      * Expands this region if it was previously collapsed.
33767      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33768      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33769      */
33770     /*
33771     expand : function(e, skipAnim){
33772         if(e) {
33773             e.stopPropagation();
33774         }
33775         if(!this.collapsed || this.el.hasActiveFx()) {
33776             return;
33777         }
33778         if(this.isSlid){
33779             this.afterSlideIn();
33780             skipAnim = true;
33781         }
33782         this.collapsed = false;
33783         if(this.config.animate && skipAnim !== true){
33784             this.animateExpand();
33785         }else{
33786             this.el.show();
33787             if(this.split){
33788                 this.split.el.show();
33789             }
33790             this.collapsedEl.setLocation(-2000,-2000);
33791             this.collapsedEl.hide();
33792             this.fireEvent("invalidated", this);
33793             this.fireEvent("expanded", this);
33794         }
33795     },
33796 */
33797     animateExpand : function(){
33798         // overridden
33799     },
33800
33801     initTabs : function()
33802     {
33803         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
33804         
33805         var ts = new Roo.bootstrap.panel.Tabs({
33806                 el: this.bodyEl.dom,
33807                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
33808                 disableTooltips: this.config.disableTabTips,
33809                 toolbar : this.config.toolbar
33810             });
33811         
33812         if(this.config.hideTabs){
33813             ts.stripWrap.setDisplayed(false);
33814         }
33815         this.tabs = ts;
33816         ts.resizeTabs = this.config.resizeTabs === true;
33817         ts.minTabWidth = this.config.minTabWidth || 40;
33818         ts.maxTabWidth = this.config.maxTabWidth || 250;
33819         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33820         ts.monitorResize = false;
33821         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
33822         ts.bodyEl.addClass('roo-layout-tabs-body');
33823         this.panels.each(this.initPanelAsTab, this);
33824     },
33825
33826     initPanelAsTab : function(panel){
33827         var ti = this.tabs.addTab(
33828             panel.getEl().id,
33829             panel.getTitle(),
33830             null,
33831             this.config.closeOnTab && panel.isClosable(),
33832             panel.tpl
33833         );
33834         if(panel.tabTip !== undefined){
33835             ti.setTooltip(panel.tabTip);
33836         }
33837         ti.on("activate", function(){
33838               this.setActivePanel(panel);
33839         }, this);
33840         
33841         if(this.config.closeOnTab){
33842             ti.on("beforeclose", function(t, e){
33843                 e.cancel = true;
33844                 this.remove(panel);
33845             }, this);
33846         }
33847         
33848         panel.tabItem = ti;
33849         
33850         return ti;
33851     },
33852
33853     updatePanelTitle : function(panel, title)
33854     {
33855         if(this.activePanel == panel){
33856             this.updateTitle(title);
33857         }
33858         if(this.tabs){
33859             var ti = this.tabs.getTab(panel.getEl().id);
33860             ti.setText(title);
33861             if(panel.tabTip !== undefined){
33862                 ti.setTooltip(panel.tabTip);
33863             }
33864         }
33865     },
33866
33867     updateTitle : function(title){
33868         if(this.titleTextEl && !this.config.title){
33869             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
33870         }
33871     },
33872
33873     setActivePanel : function(panel)
33874     {
33875         panel = this.getPanel(panel);
33876         if(this.activePanel && this.activePanel != panel){
33877             this.activePanel.setActiveState(false);
33878         }
33879         this.activePanel = panel;
33880         panel.setActiveState(true);
33881         if(this.panelSize){
33882             panel.setSize(this.panelSize.width, this.panelSize.height);
33883         }
33884         if(this.closeBtn){
33885             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33886         }
33887         this.updateTitle(panel.getTitle());
33888         if(this.tabs){
33889             this.fireEvent("invalidated", this);
33890         }
33891         this.fireEvent("panelactivated", this, panel);
33892     },
33893
33894     /**
33895      * Shows the specified panel.
33896      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33897      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33898      */
33899     showPanel : function(panel)
33900     {
33901         panel = this.getPanel(panel);
33902         if(panel){
33903             if(this.tabs){
33904                 var tab = this.tabs.getTab(panel.getEl().id);
33905                 if(tab.isHidden()){
33906                     this.tabs.unhideTab(tab.id);
33907                 }
33908                 tab.activate();
33909             }else{
33910                 this.setActivePanel(panel);
33911             }
33912         }
33913         return panel;
33914     },
33915
33916     /**
33917      * Get the active panel for this region.
33918      * @return {Roo.ContentPanel} The active panel or null
33919      */
33920     getActivePanel : function(){
33921         return this.activePanel;
33922     },
33923
33924     validateVisibility : function(){
33925         if(this.panels.getCount() < 1){
33926             this.updateTitle("&#160;");
33927             this.closeBtn.hide();
33928             this.hide();
33929         }else{
33930             if(!this.isVisible()){
33931                 this.show();
33932             }
33933         }
33934     },
33935
33936     /**
33937      * Adds the passed ContentPanel(s) to this region.
33938      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33939      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33940      */
33941     add : function(panel)
33942     {
33943         if(arguments.length > 1){
33944             for(var i = 0, len = arguments.length; i < len; i++) {
33945                 this.add(arguments[i]);
33946             }
33947             return null;
33948         }
33949         
33950         // if we have not been rendered yet, then we can not really do much of this..
33951         if (!this.bodyEl) {
33952             this.unrendered_panels.push(panel);
33953             return panel;
33954         }
33955         
33956         
33957         
33958         
33959         if(this.hasPanel(panel)){
33960             this.showPanel(panel);
33961             return panel;
33962         }
33963         panel.setRegion(this);
33964         this.panels.add(panel);
33965        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33966             // sinle panel - no tab...?? would it not be better to render it with the tabs,
33967             // and hide them... ???
33968             this.bodyEl.dom.appendChild(panel.getEl().dom);
33969             if(panel.background !== true){
33970                 this.setActivePanel(panel);
33971             }
33972             this.fireEvent("paneladded", this, panel);
33973             return panel;
33974         }
33975         */
33976         if(!this.tabs){
33977             this.initTabs();
33978         }else{
33979             this.initPanelAsTab(panel);
33980         }
33981         
33982         
33983         if(panel.background !== true){
33984             this.tabs.activate(panel.getEl().id);
33985         }
33986         this.fireEvent("paneladded", this, panel);
33987         return panel;
33988     },
33989
33990     /**
33991      * Hides the tab for the specified panel.
33992      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33993      */
33994     hidePanel : function(panel){
33995         if(this.tabs && (panel = this.getPanel(panel))){
33996             this.tabs.hideTab(panel.getEl().id);
33997         }
33998     },
33999
34000     /**
34001      * Unhides the tab for a previously hidden panel.
34002      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34003      */
34004     unhidePanel : function(panel){
34005         if(this.tabs && (panel = this.getPanel(panel))){
34006             this.tabs.unhideTab(panel.getEl().id);
34007         }
34008     },
34009
34010     clearPanels : function(){
34011         while(this.panels.getCount() > 0){
34012              this.remove(this.panels.first());
34013         }
34014     },
34015
34016     /**
34017      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34018      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34019      * @param {Boolean} preservePanel Overrides the config preservePanel option
34020      * @return {Roo.ContentPanel} The panel that was removed
34021      */
34022     remove : function(panel, preservePanel)
34023     {
34024         panel = this.getPanel(panel);
34025         if(!panel){
34026             return null;
34027         }
34028         var e = {};
34029         this.fireEvent("beforeremove", this, panel, e);
34030         if(e.cancel === true){
34031             return null;
34032         }
34033         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34034         var panelId = panel.getId();
34035         this.panels.removeKey(panelId);
34036         if(preservePanel){
34037             document.body.appendChild(panel.getEl().dom);
34038         }
34039         if(this.tabs){
34040             this.tabs.removeTab(panel.getEl().id);
34041         }else if (!preservePanel){
34042             this.bodyEl.dom.removeChild(panel.getEl().dom);
34043         }
34044         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
34045             var p = this.panels.first();
34046             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
34047             tempEl.appendChild(p.getEl().dom);
34048             this.bodyEl.update("");
34049             this.bodyEl.dom.appendChild(p.getEl().dom);
34050             tempEl = null;
34051             this.updateTitle(p.getTitle());
34052             this.tabs = null;
34053             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34054             this.setActivePanel(p);
34055         }
34056         panel.setRegion(null);
34057         if(this.activePanel == panel){
34058             this.activePanel = null;
34059         }
34060         if(this.config.autoDestroy !== false && preservePanel !== true){
34061             try{panel.destroy();}catch(e){}
34062         }
34063         this.fireEvent("panelremoved", this, panel);
34064         return panel;
34065     },
34066
34067     /**
34068      * Returns the TabPanel component used by this region
34069      * @return {Roo.TabPanel}
34070      */
34071     getTabs : function(){
34072         return this.tabs;
34073     },
34074
34075     createTool : function(parentEl, className){
34076         var btn = Roo.DomHelper.append(parentEl, {
34077             tag: "div",
34078             cls: "x-layout-tools-button",
34079             children: [ {
34080                 tag: "div",
34081                 cls: "roo-layout-tools-button-inner " + className,
34082                 html: "&#160;"
34083             }]
34084         }, true);
34085         btn.addClassOnOver("roo-layout-tools-button-over");
34086         return btn;
34087     }
34088 });/*
34089  * Based on:
34090  * Ext JS Library 1.1.1
34091  * Copyright(c) 2006-2007, Ext JS, LLC.
34092  *
34093  * Originally Released Under LGPL - original licence link has changed is not relivant.
34094  *
34095  * Fork - LGPL
34096  * <script type="text/javascript">
34097  */
34098  
34099
34100
34101 /**
34102  * @class Roo.SplitLayoutRegion
34103  * @extends Roo.LayoutRegion
34104  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34105  */
34106 Roo.bootstrap.layout.Split = function(config){
34107     this.cursor = config.cursor;
34108     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
34109 };
34110
34111 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
34112 {
34113     splitTip : "Drag to resize.",
34114     collapsibleSplitTip : "Drag to resize. Double click to hide.",
34115     useSplitTips : false,
34116
34117     applyConfig : function(config){
34118         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
34119     },
34120     
34121     onRender : function(ctr,pos) {
34122         
34123         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
34124         if(!this.config.split){
34125             return;
34126         }
34127         if(!this.split){
34128             
34129             var splitEl = Roo.DomHelper.append(ctr.dom,  {
34130                             tag: "div",
34131                             id: this.el.id + "-split",
34132                             cls: "roo-layout-split roo-layout-split-"+this.position,
34133                             html: "&#160;"
34134             });
34135             /** The SplitBar for this region 
34136             * @type Roo.SplitBar */
34137             // does not exist yet...
34138             Roo.log([this.position, this.orientation]);
34139             
34140             this.split = new Roo.bootstrap.SplitBar({
34141                 dragElement : splitEl,
34142                 resizingElement: this.el,
34143                 orientation : this.orientation
34144             });
34145             
34146             this.split.on("moved", this.onSplitMove, this);
34147             this.split.useShim = this.config.useShim === true;
34148             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34149             if(this.useSplitTips){
34150                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34151             }
34152             //if(config.collapsible){
34153             //    this.split.el.on("dblclick", this.collapse,  this);
34154             //}
34155         }
34156         if(typeof this.config.minSize != "undefined"){
34157             this.split.minSize = this.config.minSize;
34158         }
34159         if(typeof this.config.maxSize != "undefined"){
34160             this.split.maxSize = this.config.maxSize;
34161         }
34162         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
34163             this.hideSplitter();
34164         }
34165         
34166     },
34167
34168     getHMaxSize : function(){
34169          var cmax = this.config.maxSize || 10000;
34170          var center = this.mgr.getRegion("center");
34171          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34172     },
34173
34174     getVMaxSize : function(){
34175          var cmax = this.config.maxSize || 10000;
34176          var center = this.mgr.getRegion("center");
34177          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34178     },
34179
34180     onSplitMove : function(split, newSize){
34181         this.fireEvent("resized", this, newSize);
34182     },
34183     
34184     /** 
34185      * Returns the {@link Roo.SplitBar} for this region.
34186      * @return {Roo.SplitBar}
34187      */
34188     getSplitBar : function(){
34189         return this.split;
34190     },
34191     
34192     hide : function(){
34193         this.hideSplitter();
34194         Roo.bootstrap.layout.Split.superclass.hide.call(this);
34195     },
34196
34197     hideSplitter : function(){
34198         if(this.split){
34199             this.split.el.setLocation(-2000,-2000);
34200             this.split.el.hide();
34201         }
34202     },
34203
34204     show : function(){
34205         if(this.split){
34206             this.split.el.show();
34207         }
34208         Roo.bootstrap.layout.Split.superclass.show.call(this);
34209     },
34210     
34211     beforeSlide: function(){
34212         if(Roo.isGecko){// firefox overflow auto bug workaround
34213             this.bodyEl.clip();
34214             if(this.tabs) {
34215                 this.tabs.bodyEl.clip();
34216             }
34217             if(this.activePanel){
34218                 this.activePanel.getEl().clip();
34219                 
34220                 if(this.activePanel.beforeSlide){
34221                     this.activePanel.beforeSlide();
34222                 }
34223             }
34224         }
34225     },
34226     
34227     afterSlide : function(){
34228         if(Roo.isGecko){// firefox overflow auto bug workaround
34229             this.bodyEl.unclip();
34230             if(this.tabs) {
34231                 this.tabs.bodyEl.unclip();
34232             }
34233             if(this.activePanel){
34234                 this.activePanel.getEl().unclip();
34235                 if(this.activePanel.afterSlide){
34236                     this.activePanel.afterSlide();
34237                 }
34238             }
34239         }
34240     },
34241
34242     initAutoHide : function(){
34243         if(this.autoHide !== false){
34244             if(!this.autoHideHd){
34245                 var st = new Roo.util.DelayedTask(this.slideIn, this);
34246                 this.autoHideHd = {
34247                     "mouseout": function(e){
34248                         if(!e.within(this.el, true)){
34249                             st.delay(500);
34250                         }
34251                     },
34252                     "mouseover" : function(e){
34253                         st.cancel();
34254                     },
34255                     scope : this
34256                 };
34257             }
34258             this.el.on(this.autoHideHd);
34259         }
34260     },
34261
34262     clearAutoHide : function(){
34263         if(this.autoHide !== false){
34264             this.el.un("mouseout", this.autoHideHd.mouseout);
34265             this.el.un("mouseover", this.autoHideHd.mouseover);
34266         }
34267     },
34268
34269     clearMonitor : function(){
34270         Roo.get(document).un("click", this.slideInIf, this);
34271     },
34272
34273     // these names are backwards but not changed for compat
34274     slideOut : function(){
34275         if(this.isSlid || this.el.hasActiveFx()){
34276             return;
34277         }
34278         this.isSlid = true;
34279         if(this.collapseBtn){
34280             this.collapseBtn.hide();
34281         }
34282         this.closeBtnState = this.closeBtn.getStyle('display');
34283         this.closeBtn.hide();
34284         if(this.stickBtn){
34285             this.stickBtn.show();
34286         }
34287         this.el.show();
34288         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34289         this.beforeSlide();
34290         this.el.setStyle("z-index", 10001);
34291         this.el.slideIn(this.getSlideAnchor(), {
34292             callback: function(){
34293                 this.afterSlide();
34294                 this.initAutoHide();
34295                 Roo.get(document).on("click", this.slideInIf, this);
34296                 this.fireEvent("slideshow", this);
34297             },
34298             scope: this,
34299             block: true
34300         });
34301     },
34302
34303     afterSlideIn : function(){
34304         this.clearAutoHide();
34305         this.isSlid = false;
34306         this.clearMonitor();
34307         this.el.setStyle("z-index", "");
34308         if(this.collapseBtn){
34309             this.collapseBtn.show();
34310         }
34311         this.closeBtn.setStyle('display', this.closeBtnState);
34312         if(this.stickBtn){
34313             this.stickBtn.hide();
34314         }
34315         this.fireEvent("slidehide", this);
34316     },
34317
34318     slideIn : function(cb){
34319         if(!this.isSlid || this.el.hasActiveFx()){
34320             Roo.callback(cb);
34321             return;
34322         }
34323         this.isSlid = false;
34324         this.beforeSlide();
34325         this.el.slideOut(this.getSlideAnchor(), {
34326             callback: function(){
34327                 this.el.setLeftTop(-10000, -10000);
34328                 this.afterSlide();
34329                 this.afterSlideIn();
34330                 Roo.callback(cb);
34331             },
34332             scope: this,
34333             block: true
34334         });
34335     },
34336     
34337     slideInIf : function(e){
34338         if(!e.within(this.el)){
34339             this.slideIn();
34340         }
34341     },
34342
34343     animateCollapse : function(){
34344         this.beforeSlide();
34345         this.el.setStyle("z-index", 20000);
34346         var anchor = this.getSlideAnchor();
34347         this.el.slideOut(anchor, {
34348             callback : function(){
34349                 this.el.setStyle("z-index", "");
34350                 this.collapsedEl.slideIn(anchor, {duration:.3});
34351                 this.afterSlide();
34352                 this.el.setLocation(-10000,-10000);
34353                 this.el.hide();
34354                 this.fireEvent("collapsed", this);
34355             },
34356             scope: this,
34357             block: true
34358         });
34359     },
34360
34361     animateExpand : function(){
34362         this.beforeSlide();
34363         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34364         this.el.setStyle("z-index", 20000);
34365         this.collapsedEl.hide({
34366             duration:.1
34367         });
34368         this.el.slideIn(this.getSlideAnchor(), {
34369             callback : function(){
34370                 this.el.setStyle("z-index", "");
34371                 this.afterSlide();
34372                 if(this.split){
34373                     this.split.el.show();
34374                 }
34375                 this.fireEvent("invalidated", this);
34376                 this.fireEvent("expanded", this);
34377             },
34378             scope: this,
34379             block: true
34380         });
34381     },
34382
34383     anchors : {
34384         "west" : "left",
34385         "east" : "right",
34386         "north" : "top",
34387         "south" : "bottom"
34388     },
34389
34390     sanchors : {
34391         "west" : "l",
34392         "east" : "r",
34393         "north" : "t",
34394         "south" : "b"
34395     },
34396
34397     canchors : {
34398         "west" : "tl-tr",
34399         "east" : "tr-tl",
34400         "north" : "tl-bl",
34401         "south" : "bl-tl"
34402     },
34403
34404     getAnchor : function(){
34405         return this.anchors[this.position];
34406     },
34407
34408     getCollapseAnchor : function(){
34409         return this.canchors[this.position];
34410     },
34411
34412     getSlideAnchor : function(){
34413         return this.sanchors[this.position];
34414     },
34415
34416     getAlignAdj : function(){
34417         var cm = this.cmargins;
34418         switch(this.position){
34419             case "west":
34420                 return [0, 0];
34421             break;
34422             case "east":
34423                 return [0, 0];
34424             break;
34425             case "north":
34426                 return [0, 0];
34427             break;
34428             case "south":
34429                 return [0, 0];
34430             break;
34431         }
34432     },
34433
34434     getExpandAdj : function(){
34435         var c = this.collapsedEl, cm = this.cmargins;
34436         switch(this.position){
34437             case "west":
34438                 return [-(cm.right+c.getWidth()+cm.left), 0];
34439             break;
34440             case "east":
34441                 return [cm.right+c.getWidth()+cm.left, 0];
34442             break;
34443             case "north":
34444                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34445             break;
34446             case "south":
34447                 return [0, cm.top+cm.bottom+c.getHeight()];
34448             break;
34449         }
34450     }
34451 });/*
34452  * Based on:
34453  * Ext JS Library 1.1.1
34454  * Copyright(c) 2006-2007, Ext JS, LLC.
34455  *
34456  * Originally Released Under LGPL - original licence link has changed is not relivant.
34457  *
34458  * Fork - LGPL
34459  * <script type="text/javascript">
34460  */
34461 /*
34462  * These classes are private internal classes
34463  */
34464 Roo.bootstrap.layout.Center = function(config){
34465     config.region = "center";
34466     Roo.bootstrap.layout.Region.call(this, config);
34467     this.visible = true;
34468     this.minWidth = config.minWidth || 20;
34469     this.minHeight = config.minHeight || 20;
34470 };
34471
34472 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
34473     hide : function(){
34474         // center panel can't be hidden
34475     },
34476     
34477     show : function(){
34478         // center panel can't be hidden
34479     },
34480     
34481     getMinWidth: function(){
34482         return this.minWidth;
34483     },
34484     
34485     getMinHeight: function(){
34486         return this.minHeight;
34487     }
34488 });
34489
34490
34491
34492
34493  
34494
34495
34496
34497
34498
34499 Roo.bootstrap.layout.North = function(config)
34500 {
34501     config.region = 'north';
34502     config.cursor = 'n-resize';
34503     
34504     Roo.bootstrap.layout.Split.call(this, config);
34505     
34506     
34507     if(this.split){
34508         this.split.placement = Roo.bootstrap.SplitBar.TOP;
34509         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34510         this.split.el.addClass("roo-layout-split-v");
34511     }
34512     var size = config.initialSize || config.height;
34513     if(typeof size != "undefined"){
34514         this.el.setHeight(size);
34515     }
34516 };
34517 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
34518 {
34519     orientation: Roo.bootstrap.SplitBar.VERTICAL,
34520     
34521     
34522     
34523     getBox : function(){
34524         if(this.collapsed){
34525             return this.collapsedEl.getBox();
34526         }
34527         var box = this.el.getBox();
34528         if(this.split){
34529             box.height += this.split.el.getHeight();
34530         }
34531         return box;
34532     },
34533     
34534     updateBox : function(box){
34535         if(this.split && !this.collapsed){
34536             box.height -= this.split.el.getHeight();
34537             this.split.el.setLeft(box.x);
34538             this.split.el.setTop(box.y+box.height);
34539             this.split.el.setWidth(box.width);
34540         }
34541         if(this.collapsed){
34542             this.updateBody(box.width, null);
34543         }
34544         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34545     }
34546 });
34547
34548
34549
34550
34551
34552 Roo.bootstrap.layout.South = function(config){
34553     config.region = 'south';
34554     config.cursor = 's-resize';
34555     Roo.bootstrap.layout.Split.call(this, config);
34556     if(this.split){
34557         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
34558         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34559         this.split.el.addClass("roo-layout-split-v");
34560     }
34561     var size = config.initialSize || config.height;
34562     if(typeof size != "undefined"){
34563         this.el.setHeight(size);
34564     }
34565 };
34566
34567 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
34568     orientation: Roo.bootstrap.SplitBar.VERTICAL,
34569     getBox : function(){
34570         if(this.collapsed){
34571             return this.collapsedEl.getBox();
34572         }
34573         var box = this.el.getBox();
34574         if(this.split){
34575             var sh = this.split.el.getHeight();
34576             box.height += sh;
34577             box.y -= sh;
34578         }
34579         return box;
34580     },
34581     
34582     updateBox : function(box){
34583         if(this.split && !this.collapsed){
34584             var sh = this.split.el.getHeight();
34585             box.height -= sh;
34586             box.y += sh;
34587             this.split.el.setLeft(box.x);
34588             this.split.el.setTop(box.y-sh);
34589             this.split.el.setWidth(box.width);
34590         }
34591         if(this.collapsed){
34592             this.updateBody(box.width, null);
34593         }
34594         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34595     }
34596 });
34597
34598 Roo.bootstrap.layout.East = function(config){
34599     config.region = "east";
34600     config.cursor = "e-resize";
34601     Roo.bootstrap.layout.Split.call(this, config);
34602     if(this.split){
34603         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
34604         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34605         this.split.el.addClass("roo-layout-split-h");
34606     }
34607     var size = config.initialSize || config.width;
34608     if(typeof size != "undefined"){
34609         this.el.setWidth(size);
34610     }
34611 };
34612 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
34613     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34614     getBox : function(){
34615         if(this.collapsed){
34616             return this.collapsedEl.getBox();
34617         }
34618         var box = this.el.getBox();
34619         if(this.split){
34620             var sw = this.split.el.getWidth();
34621             box.width += sw;
34622             box.x -= sw;
34623         }
34624         return box;
34625     },
34626
34627     updateBox : function(box){
34628         if(this.split && !this.collapsed){
34629             var sw = this.split.el.getWidth();
34630             box.width -= sw;
34631             this.split.el.setLeft(box.x);
34632             this.split.el.setTop(box.y);
34633             this.split.el.setHeight(box.height);
34634             box.x += sw;
34635         }
34636         if(this.collapsed){
34637             this.updateBody(null, box.height);
34638         }
34639         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34640     }
34641 });
34642
34643 Roo.bootstrap.layout.West = function(config){
34644     config.region = "west";
34645     config.cursor = "w-resize";
34646     
34647     Roo.bootstrap.layout.Split.call(this, config);
34648     if(this.split){
34649         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
34650         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34651         this.split.el.addClass("roo-layout-split-h");
34652     }
34653     
34654 };
34655 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
34656     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34657     
34658     onRender: function(ctr, pos)
34659     {
34660         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
34661         var size = this.config.initialSize || this.config.width;
34662         if(typeof size != "undefined"){
34663             this.el.setWidth(size);
34664         }
34665     },
34666     
34667     getBox : function(){
34668         if(this.collapsed){
34669             return this.collapsedEl.getBox();
34670         }
34671         var box = this.el.getBox();
34672         if(this.split){
34673             box.width += this.split.el.getWidth();
34674         }
34675         return box;
34676     },
34677     
34678     updateBox : function(box){
34679         if(this.split && !this.collapsed){
34680             var sw = this.split.el.getWidth();
34681             box.width -= sw;
34682             this.split.el.setLeft(box.x+box.width);
34683             this.split.el.setTop(box.y);
34684             this.split.el.setHeight(box.height);
34685         }
34686         if(this.collapsed){
34687             this.updateBody(null, box.height);
34688         }
34689         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34690     }
34691 });
34692 Roo.namespace("Roo.bootstrap.panel");/*
34693  * Based on:
34694  * Ext JS Library 1.1.1
34695  * Copyright(c) 2006-2007, Ext JS, LLC.
34696  *
34697  * Originally Released Under LGPL - original licence link has changed is not relivant.
34698  *
34699  * Fork - LGPL
34700  * <script type="text/javascript">
34701  */
34702 /**
34703  * @class Roo.ContentPanel
34704  * @extends Roo.util.Observable
34705  * A basic ContentPanel element.
34706  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
34707  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
34708  * @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
34709  * @cfg {Boolean}   closable      True if the panel can be closed/removed
34710  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
34711  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34712  * @cfg {Toolbar}   toolbar       A toolbar for this panel
34713  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
34714  * @cfg {String} title          The title for this panel
34715  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34716  * @cfg {String} url            Calls {@link #setUrl} with this value
34717  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34718  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
34719  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
34720  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
34721  * @cfg {Boolean} badges render the badges
34722
34723  * @constructor
34724  * Create a new ContentPanel.
34725  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34726  * @param {String/Object} config A string to set only the title or a config object
34727  * @param {String} content (optional) Set the HTML content for this panel
34728  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34729  */
34730 Roo.bootstrap.panel.Content = function( config){
34731     
34732     this.tpl = config.tpl || false;
34733     
34734     var el = config.el;
34735     var content = config.content;
34736
34737     if(config.autoCreate){ // xtype is available if this is called from factory
34738         el = Roo.id();
34739     }
34740     this.el = Roo.get(el);
34741     if(!this.el && config && config.autoCreate){
34742         if(typeof config.autoCreate == "object"){
34743             if(!config.autoCreate.id){
34744                 config.autoCreate.id = config.id||el;
34745             }
34746             this.el = Roo.DomHelper.append(document.body,
34747                         config.autoCreate, true);
34748         }else{
34749             var elcfg =  {   tag: "div",
34750                             cls: "roo-layout-inactive-content",
34751                             id: config.id||el
34752                             };
34753             if (config.html) {
34754                 elcfg.html = config.html;
34755                 
34756             }
34757                         
34758             this.el = Roo.DomHelper.append(document.body, elcfg , true);
34759         }
34760     } 
34761     this.closable = false;
34762     this.loaded = false;
34763     this.active = false;
34764    
34765       
34766     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
34767         
34768         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
34769         
34770         this.wrapEl = this.el; //this.el.wrap();
34771         var ti = [];
34772         if (config.toolbar.items) {
34773             ti = config.toolbar.items ;
34774             delete config.toolbar.items ;
34775         }
34776         
34777         var nitems = [];
34778         this.toolbar.render(this.wrapEl, 'before');
34779         for(var i =0;i < ti.length;i++) {
34780           //  Roo.log(['add child', items[i]]);
34781             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34782         }
34783         this.toolbar.items = nitems;
34784         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
34785         delete config.toolbar;
34786         
34787     }
34788     /*
34789     // xtype created footer. - not sure if will work as we normally have to render first..
34790     if (this.footer && !this.footer.el && this.footer.xtype) {
34791         if (!this.wrapEl) {
34792             this.wrapEl = this.el.wrap();
34793         }
34794     
34795         this.footer.container = this.wrapEl.createChild();
34796          
34797         this.footer = Roo.factory(this.footer, Roo);
34798         
34799     }
34800     */
34801     
34802      if(typeof config == "string"){
34803         this.title = config;
34804     }else{
34805         Roo.apply(this, config);
34806     }
34807     
34808     if(this.resizeEl){
34809         this.resizeEl = Roo.get(this.resizeEl, true);
34810     }else{
34811         this.resizeEl = this.el;
34812     }
34813     // handle view.xtype
34814     
34815  
34816     
34817     
34818     this.addEvents({
34819         /**
34820          * @event activate
34821          * Fires when this panel is activated. 
34822          * @param {Roo.ContentPanel} this
34823          */
34824         "activate" : true,
34825         /**
34826          * @event deactivate
34827          * Fires when this panel is activated. 
34828          * @param {Roo.ContentPanel} this
34829          */
34830         "deactivate" : true,
34831
34832         /**
34833          * @event resize
34834          * Fires when this panel is resized if fitToFrame is true.
34835          * @param {Roo.ContentPanel} this
34836          * @param {Number} width The width after any component adjustments
34837          * @param {Number} height The height after any component adjustments
34838          */
34839         "resize" : true,
34840         
34841          /**
34842          * @event render
34843          * Fires when this tab is created
34844          * @param {Roo.ContentPanel} this
34845          */
34846         "render" : true
34847         
34848         
34849         
34850     });
34851     
34852
34853     
34854     
34855     if(this.autoScroll){
34856         this.resizeEl.setStyle("overflow", "auto");
34857     } else {
34858         // fix randome scrolling
34859         //this.el.on('scroll', function() {
34860         //    Roo.log('fix random scolling');
34861         //    this.scrollTo('top',0); 
34862         //});
34863     }
34864     content = content || this.content;
34865     if(content){
34866         this.setContent(content);
34867     }
34868     if(config && config.url){
34869         this.setUrl(this.url, this.params, this.loadOnce);
34870     }
34871     
34872     
34873     
34874     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
34875     
34876     if (this.view && typeof(this.view.xtype) != 'undefined') {
34877         this.view.el = this.el.appendChild(document.createElement("div"));
34878         this.view = Roo.factory(this.view); 
34879         this.view.render  &&  this.view.render(false, '');  
34880     }
34881     
34882     
34883     this.fireEvent('render', this);
34884 };
34885
34886 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
34887     
34888     tabTip : '',
34889     
34890     setRegion : function(region){
34891         this.region = region;
34892         this.setActiveClass(region && !this.background);
34893     },
34894     
34895     
34896     setActiveClass: function(state)
34897     {
34898         if(state){
34899            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
34900            this.el.setStyle('position','relative');
34901         }else{
34902            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
34903            this.el.setStyle('position', 'absolute');
34904         } 
34905     },
34906     
34907     /**
34908      * Returns the toolbar for this Panel if one was configured. 
34909      * @return {Roo.Toolbar} 
34910      */
34911     getToolbar : function(){
34912         return this.toolbar;
34913     },
34914     
34915     setActiveState : function(active)
34916     {
34917         this.active = active;
34918         this.setActiveClass(active);
34919         if(!active){
34920             this.fireEvent("deactivate", this);
34921         }else{
34922             this.fireEvent("activate", this);
34923         }
34924     },
34925     /**
34926      * Updates this panel's element
34927      * @param {String} content The new content
34928      * @param {Boolean} loadScripts (optional) true to look for and process scripts
34929     */
34930     setContent : function(content, loadScripts){
34931         this.el.update(content, loadScripts);
34932     },
34933
34934     ignoreResize : function(w, h){
34935         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34936             return true;
34937         }else{
34938             this.lastSize = {width: w, height: h};
34939             return false;
34940         }
34941     },
34942     /**
34943      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34944      * @return {Roo.UpdateManager} The UpdateManager
34945      */
34946     getUpdateManager : function(){
34947         return this.el.getUpdateManager();
34948     },
34949      /**
34950      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34951      * @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:
34952 <pre><code>
34953 panel.load({
34954     url: "your-url.php",
34955     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34956     callback: yourFunction,
34957     scope: yourObject, //(optional scope)
34958     discardUrl: false,
34959     nocache: false,
34960     text: "Loading...",
34961     timeout: 30,
34962     scripts: false
34963 });
34964 </code></pre>
34965      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34966      * 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.
34967      * @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}
34968      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34969      * @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.
34970      * @return {Roo.ContentPanel} this
34971      */
34972     load : function(){
34973         var um = this.el.getUpdateManager();
34974         um.update.apply(um, arguments);
34975         return this;
34976     },
34977
34978
34979     /**
34980      * 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.
34981      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34982      * @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)
34983      * @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)
34984      * @return {Roo.UpdateManager} The UpdateManager
34985      */
34986     setUrl : function(url, params, loadOnce){
34987         if(this.refreshDelegate){
34988             this.removeListener("activate", this.refreshDelegate);
34989         }
34990         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34991         this.on("activate", this.refreshDelegate);
34992         return this.el.getUpdateManager();
34993     },
34994     
34995     _handleRefresh : function(url, params, loadOnce){
34996         if(!loadOnce || !this.loaded){
34997             var updater = this.el.getUpdateManager();
34998             updater.update(url, params, this._setLoaded.createDelegate(this));
34999         }
35000     },
35001     
35002     _setLoaded : function(){
35003         this.loaded = true;
35004     }, 
35005     
35006     /**
35007      * Returns this panel's id
35008      * @return {String} 
35009      */
35010     getId : function(){
35011         return this.el.id;
35012     },
35013     
35014     /** 
35015      * Returns this panel's element - used by regiosn to add.
35016      * @return {Roo.Element} 
35017      */
35018     getEl : function(){
35019         return this.wrapEl || this.el;
35020     },
35021     
35022    
35023     
35024     adjustForComponents : function(width, height)
35025     {
35026         //Roo.log('adjustForComponents ');
35027         if(this.resizeEl != this.el){
35028             width -= this.el.getFrameWidth('lr');
35029             height -= this.el.getFrameWidth('tb');
35030         }
35031         if(this.toolbar){
35032             var te = this.toolbar.getEl();
35033             height -= te.getHeight();
35034             te.setWidth(width);
35035         }
35036         if(this.footer){
35037             var te = this.footer.getEl();
35038             Roo.log("footer:" + te.getHeight());
35039             
35040             height -= te.getHeight();
35041             te.setWidth(width);
35042         }
35043         
35044         
35045         if(this.adjustments){
35046             width += this.adjustments[0];
35047             height += this.adjustments[1];
35048         }
35049         return {"width": width, "height": height};
35050     },
35051     
35052     setSize : function(width, height){
35053         if(this.fitToFrame && !this.ignoreResize(width, height)){
35054             if(this.fitContainer && this.resizeEl != this.el){
35055                 this.el.setSize(width, height);
35056             }
35057             var size = this.adjustForComponents(width, height);
35058             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
35059             this.fireEvent('resize', this, size.width, size.height);
35060         }
35061     },
35062     
35063     /**
35064      * Returns this panel's title
35065      * @return {String} 
35066      */
35067     getTitle : function(){
35068         return this.title;
35069     },
35070     
35071     /**
35072      * Set this panel's title
35073      * @param {String} title
35074      */
35075     setTitle : function(title){
35076         this.title = title;
35077         if(this.region){
35078             this.region.updatePanelTitle(this, title);
35079         }
35080     },
35081     
35082     /**
35083      * Returns true is this panel was configured to be closable
35084      * @return {Boolean} 
35085      */
35086     isClosable : function(){
35087         return this.closable;
35088     },
35089     
35090     beforeSlide : function(){
35091         this.el.clip();
35092         this.resizeEl.clip();
35093     },
35094     
35095     afterSlide : function(){
35096         this.el.unclip();
35097         this.resizeEl.unclip();
35098     },
35099     
35100     /**
35101      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
35102      *   Will fail silently if the {@link #setUrl} method has not been called.
35103      *   This does not activate the panel, just updates its content.
35104      */
35105     refresh : function(){
35106         if(this.refreshDelegate){
35107            this.loaded = false;
35108            this.refreshDelegate();
35109         }
35110     },
35111     
35112     /**
35113      * Destroys this panel
35114      */
35115     destroy : function(){
35116         this.el.removeAllListeners();
35117         var tempEl = document.createElement("span");
35118         tempEl.appendChild(this.el.dom);
35119         tempEl.innerHTML = "";
35120         this.el.remove();
35121         this.el = null;
35122     },
35123     
35124     /**
35125      * form - if the content panel contains a form - this is a reference to it.
35126      * @type {Roo.form.Form}
35127      */
35128     form : false,
35129     /**
35130      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35131      *    This contains a reference to it.
35132      * @type {Roo.View}
35133      */
35134     view : false,
35135     
35136       /**
35137      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35138      * <pre><code>
35139
35140 layout.addxtype({
35141        xtype : 'Form',
35142        items: [ .... ]
35143    }
35144 );
35145
35146 </code></pre>
35147      * @param {Object} cfg Xtype definition of item to add.
35148      */
35149     
35150     
35151     getChildContainer: function () {
35152         return this.getEl();
35153     }
35154     
35155     
35156     /*
35157         var  ret = new Roo.factory(cfg);
35158         return ret;
35159         
35160         
35161         // add form..
35162         if (cfg.xtype.match(/^Form$/)) {
35163             
35164             var el;
35165             //if (this.footer) {
35166             //    el = this.footer.container.insertSibling(false, 'before');
35167             //} else {
35168                 el = this.el.createChild();
35169             //}
35170
35171             this.form = new  Roo.form.Form(cfg);
35172             
35173             
35174             if ( this.form.allItems.length) {
35175                 this.form.render(el.dom);
35176             }
35177             return this.form;
35178         }
35179         // should only have one of theses..
35180         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35181             // views.. should not be just added - used named prop 'view''
35182             
35183             cfg.el = this.el.appendChild(document.createElement("div"));
35184             // factory?
35185             
35186             var ret = new Roo.factory(cfg);
35187              
35188              ret.render && ret.render(false, ''); // render blank..
35189             this.view = ret;
35190             return ret;
35191         }
35192         return false;
35193     }
35194     \*/
35195 });
35196  
35197 /**
35198  * @class Roo.bootstrap.panel.Grid
35199  * @extends Roo.bootstrap.panel.Content
35200  * @constructor
35201  * Create a new GridPanel.
35202  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
35203  * @param {Object} config A the config object
35204   
35205  */
35206
35207
35208
35209 Roo.bootstrap.panel.Grid = function(config)
35210 {
35211     
35212       
35213     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35214         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
35215
35216     config.el = this.wrapper;
35217     //this.el = this.wrapper;
35218     
35219       if (config.container) {
35220         // ctor'ed from a Border/panel.grid
35221         
35222         
35223         this.wrapper.setStyle("overflow", "hidden");
35224         this.wrapper.addClass('roo-grid-container');
35225
35226     }
35227     
35228     
35229     if(config.toolbar){
35230         var tool_el = this.wrapper.createChild();    
35231         this.toolbar = Roo.factory(config.toolbar);
35232         var ti = [];
35233         if (config.toolbar.items) {
35234             ti = config.toolbar.items ;
35235             delete config.toolbar.items ;
35236         }
35237         
35238         var nitems = [];
35239         this.toolbar.render(tool_el);
35240         for(var i =0;i < ti.length;i++) {
35241           //  Roo.log(['add child', items[i]]);
35242             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
35243         }
35244         this.toolbar.items = nitems;
35245         
35246         delete config.toolbar;
35247     }
35248     
35249     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
35250     config.grid.scrollBody = true;;
35251     config.grid.monitorWindowResize = false; // turn off autosizing
35252     config.grid.autoHeight = false;
35253     config.grid.autoWidth = false;
35254     
35255     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
35256     
35257     if (config.background) {
35258         // render grid on panel activation (if panel background)
35259         this.on('activate', function(gp) {
35260             if (!gp.grid.rendered) {
35261                 gp.grid.render(this.wrapper);
35262                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
35263             }
35264         });
35265             
35266     } else {
35267         this.grid.render(this.wrapper);
35268         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
35269
35270     }
35271     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
35272     // ??? needed ??? config.el = this.wrapper;
35273     
35274     
35275     
35276   
35277     // xtype created footer. - not sure if will work as we normally have to render first..
35278     if (this.footer && !this.footer.el && this.footer.xtype) {
35279         
35280         var ctr = this.grid.getView().getFooterPanel(true);
35281         this.footer.dataSource = this.grid.dataSource;
35282         this.footer = Roo.factory(this.footer, Roo);
35283         this.footer.render(ctr);
35284         
35285     }
35286     
35287     
35288     
35289     
35290      
35291 };
35292
35293 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
35294     getId : function(){
35295         return this.grid.id;
35296     },
35297     
35298     /**
35299      * Returns the grid for this panel
35300      * @return {Roo.bootstrap.Table} 
35301      */
35302     getGrid : function(){
35303         return this.grid;    
35304     },
35305     
35306     setSize : function(width, height){
35307         if(!this.ignoreResize(width, height)){
35308             var grid = this.grid;
35309             var size = this.adjustForComponents(width, height);
35310             var gridel = grid.getGridEl();
35311             gridel.setSize(size.width, size.height);
35312             /*
35313             var thd = grid.getGridEl().select('thead',true).first();
35314             var tbd = grid.getGridEl().select('tbody', true).first();
35315             if (tbd) {
35316                 tbd.setSize(width, height - thd.getHeight());
35317             }
35318             */
35319             grid.autoSize();
35320         }
35321     },
35322      
35323     
35324     
35325     beforeSlide : function(){
35326         this.grid.getView().scroller.clip();
35327     },
35328     
35329     afterSlide : function(){
35330         this.grid.getView().scroller.unclip();
35331     },
35332     
35333     destroy : function(){
35334         this.grid.destroy();
35335         delete this.grid;
35336         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
35337     }
35338 });
35339
35340 /**
35341  * @class Roo.bootstrap.panel.Nest
35342  * @extends Roo.bootstrap.panel.Content
35343  * @constructor
35344  * Create a new Panel, that can contain a layout.Border.
35345  * 
35346  * 
35347  * @param {Roo.BorderLayout} layout The layout for this panel
35348  * @param {String/Object} config A string to set only the title or a config object
35349  */
35350 Roo.bootstrap.panel.Nest = function(config)
35351 {
35352     // construct with only one argument..
35353     /* FIXME - implement nicer consturctors
35354     if (layout.layout) {
35355         config = layout;
35356         layout = config.layout;
35357         delete config.layout;
35358     }
35359     if (layout.xtype && !layout.getEl) {
35360         // then layout needs constructing..
35361         layout = Roo.factory(layout, Roo);
35362     }
35363     */
35364     
35365     config.el =  config.layout.getEl();
35366     
35367     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
35368     
35369     config.layout.monitorWindowResize = false; // turn off autosizing
35370     this.layout = config.layout;
35371     this.layout.getEl().addClass("roo-layout-nested-layout");
35372     
35373     
35374     
35375     
35376 };
35377
35378 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
35379
35380     setSize : function(width, height){
35381         if(!this.ignoreResize(width, height)){
35382             var size = this.adjustForComponents(width, height);
35383             var el = this.layout.getEl();
35384             if (size.height < 1) {
35385                 el.setWidth(size.width);   
35386             } else {
35387                 el.setSize(size.width, size.height);
35388             }
35389             var touch = el.dom.offsetWidth;
35390             this.layout.layout();
35391             // ie requires a double layout on the first pass
35392             if(Roo.isIE && !this.initialized){
35393                 this.initialized = true;
35394                 this.layout.layout();
35395             }
35396         }
35397     },
35398     
35399     // activate all subpanels if not currently active..
35400     
35401     setActiveState : function(active){
35402         this.active = active;
35403         this.setActiveClass(active);
35404         
35405         if(!active){
35406             this.fireEvent("deactivate", this);
35407             return;
35408         }
35409         
35410         this.fireEvent("activate", this);
35411         // not sure if this should happen before or after..
35412         if (!this.layout) {
35413             return; // should not happen..
35414         }
35415         var reg = false;
35416         for (var r in this.layout.regions) {
35417             reg = this.layout.getRegion(r);
35418             if (reg.getActivePanel()) {
35419                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35420                 reg.setActivePanel(reg.getActivePanel());
35421                 continue;
35422             }
35423             if (!reg.panels.length) {
35424                 continue;
35425             }
35426             reg.showPanel(reg.getPanel(0));
35427         }
35428         
35429         
35430         
35431         
35432     },
35433     
35434     /**
35435      * Returns the nested BorderLayout for this panel
35436      * @return {Roo.BorderLayout} 
35437      */
35438     getLayout : function(){
35439         return this.layout;
35440     },
35441     
35442      /**
35443      * Adds a xtype elements to the layout of the nested panel
35444      * <pre><code>
35445
35446 panel.addxtype({
35447        xtype : 'ContentPanel',
35448        region: 'west',
35449        items: [ .... ]
35450    }
35451 );
35452
35453 panel.addxtype({
35454         xtype : 'NestedLayoutPanel',
35455         region: 'west',
35456         layout: {
35457            center: { },
35458            west: { }   
35459         },
35460         items : [ ... list of content panels or nested layout panels.. ]
35461    }
35462 );
35463 </code></pre>
35464      * @param {Object} cfg Xtype definition of item to add.
35465      */
35466     addxtype : function(cfg) {
35467         return this.layout.addxtype(cfg);
35468     
35469     }
35470 });        /*
35471  * Based on:
35472  * Ext JS Library 1.1.1
35473  * Copyright(c) 2006-2007, Ext JS, LLC.
35474  *
35475  * Originally Released Under LGPL - original licence link has changed is not relivant.
35476  *
35477  * Fork - LGPL
35478  * <script type="text/javascript">
35479  */
35480 /**
35481  * @class Roo.TabPanel
35482  * @extends Roo.util.Observable
35483  * A lightweight tab container.
35484  * <br><br>
35485  * Usage:
35486  * <pre><code>
35487 // basic tabs 1, built from existing content
35488 var tabs = new Roo.TabPanel("tabs1");
35489 tabs.addTab("script", "View Script");
35490 tabs.addTab("markup", "View Markup");
35491 tabs.activate("script");
35492
35493 // more advanced tabs, built from javascript
35494 var jtabs = new Roo.TabPanel("jtabs");
35495 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
35496
35497 // set up the UpdateManager
35498 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
35499 var updater = tab2.getUpdateManager();
35500 updater.setDefaultUrl("ajax1.htm");
35501 tab2.on('activate', updater.refresh, updater, true);
35502
35503 // Use setUrl for Ajax loading
35504 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
35505 tab3.setUrl("ajax2.htm", null, true);
35506
35507 // Disabled tab
35508 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
35509 tab4.disable();
35510
35511 jtabs.activate("jtabs-1");
35512  * </code></pre>
35513  * @constructor
35514  * Create a new TabPanel.
35515  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
35516  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
35517  */
35518 Roo.bootstrap.panel.Tabs = function(config){
35519     /**
35520     * The container element for this TabPanel.
35521     * @type Roo.Element
35522     */
35523     this.el = Roo.get(config.el);
35524     delete config.el;
35525     if(config){
35526         if(typeof config == "boolean"){
35527             this.tabPosition = config ? "bottom" : "top";
35528         }else{
35529             Roo.apply(this, config);
35530         }
35531     }
35532     
35533     if(this.tabPosition == "bottom"){
35534         this.bodyEl = Roo.get(this.createBody(this.el.dom));
35535         this.el.addClass("roo-tabs-bottom");
35536     }
35537     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
35538     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
35539     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
35540     if(Roo.isIE){
35541         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
35542     }
35543     if(this.tabPosition != "bottom"){
35544         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
35545          * @type Roo.Element
35546          */
35547         this.bodyEl = Roo.get(this.createBody(this.el.dom));
35548         this.el.addClass("roo-tabs-top");
35549     }
35550     this.items = [];
35551
35552     this.bodyEl.setStyle("position", "relative");
35553
35554     this.active = null;
35555     this.activateDelegate = this.activate.createDelegate(this);
35556
35557     this.addEvents({
35558         /**
35559          * @event tabchange
35560          * Fires when the active tab changes
35561          * @param {Roo.TabPanel} this
35562          * @param {Roo.TabPanelItem} activePanel The new active tab
35563          */
35564         "tabchange": true,
35565         /**
35566          * @event beforetabchange
35567          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
35568          * @param {Roo.TabPanel} this
35569          * @param {Object} e Set cancel to true on this object to cancel the tab change
35570          * @param {Roo.TabPanelItem} tab The tab being changed to
35571          */
35572         "beforetabchange" : true
35573     });
35574
35575     Roo.EventManager.onWindowResize(this.onResize, this);
35576     this.cpad = this.el.getPadding("lr");
35577     this.hiddenCount = 0;
35578
35579
35580     // toolbar on the tabbar support...
35581     if (this.toolbar) {
35582         alert("no toolbar support yet");
35583         this.toolbar  = false;
35584         /*
35585         var tcfg = this.toolbar;
35586         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
35587         this.toolbar = new Roo.Toolbar(tcfg);
35588         if (Roo.isSafari) {
35589             var tbl = tcfg.container.child('table', true);
35590             tbl.setAttribute('width', '100%');
35591         }
35592         */
35593         
35594     }
35595    
35596
35597
35598     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
35599 };
35600
35601 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
35602     /*
35603      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
35604      */
35605     tabPosition : "top",
35606     /*
35607      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
35608      */
35609     currentTabWidth : 0,
35610     /*
35611      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
35612      */
35613     minTabWidth : 40,
35614     /*
35615      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
35616      */
35617     maxTabWidth : 250,
35618     /*
35619      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
35620      */
35621     preferredTabWidth : 175,
35622     /*
35623      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
35624      */
35625     resizeTabs : false,
35626     /*
35627      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
35628      */
35629     monitorResize : true,
35630     /*
35631      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
35632      */
35633     toolbar : false,
35634
35635     /**
35636      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
35637      * @param {String} id The id of the div to use <b>or create</b>
35638      * @param {String} text The text for the tab
35639      * @param {String} content (optional) Content to put in the TabPanelItem body
35640      * @param {Boolean} closable (optional) True to create a close icon on the tab
35641      * @return {Roo.TabPanelItem} The created TabPanelItem
35642      */
35643     addTab : function(id, text, content, closable, tpl)
35644     {
35645         var item = new Roo.bootstrap.panel.TabItem({
35646             panel: this,
35647             id : id,
35648             text : text,
35649             closable : closable,
35650             tpl : tpl
35651         });
35652         this.addTabItem(item);
35653         if(content){
35654             item.setContent(content);
35655         }
35656         return item;
35657     },
35658
35659     /**
35660      * Returns the {@link Roo.TabPanelItem} with the specified id/index
35661      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
35662      * @return {Roo.TabPanelItem}
35663      */
35664     getTab : function(id){
35665         return this.items[id];
35666     },
35667
35668     /**
35669      * Hides the {@link Roo.TabPanelItem} with the specified id/index
35670      * @param {String/Number} id The id or index of the TabPanelItem to hide.
35671      */
35672     hideTab : function(id){
35673         var t = this.items[id];
35674         if(!t.isHidden()){
35675            t.setHidden(true);
35676            this.hiddenCount++;
35677            this.autoSizeTabs();
35678         }
35679     },
35680
35681     /**
35682      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
35683      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
35684      */
35685     unhideTab : function(id){
35686         var t = this.items[id];
35687         if(t.isHidden()){
35688            t.setHidden(false);
35689            this.hiddenCount--;
35690            this.autoSizeTabs();
35691         }
35692     },
35693
35694     /**
35695      * Adds an existing {@link Roo.TabPanelItem}.
35696      * @param {Roo.TabPanelItem} item The TabPanelItem to add
35697      */
35698     addTabItem : function(item){
35699         this.items[item.id] = item;
35700         this.items.push(item);
35701       //  if(this.resizeTabs){
35702     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
35703   //         this.autoSizeTabs();
35704 //        }else{
35705 //            item.autoSize();
35706        // }
35707     },
35708
35709     /**
35710      * Removes a {@link Roo.TabPanelItem}.
35711      * @param {String/Number} id The id or index of the TabPanelItem to remove.
35712      */
35713     removeTab : function(id){
35714         var items = this.items;
35715         var tab = items[id];
35716         if(!tab) { return; }
35717         var index = items.indexOf(tab);
35718         if(this.active == tab && items.length > 1){
35719             var newTab = this.getNextAvailable(index);
35720             if(newTab) {
35721                 newTab.activate();
35722             }
35723         }
35724         this.stripEl.dom.removeChild(tab.pnode.dom);
35725         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
35726             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
35727         }
35728         items.splice(index, 1);
35729         delete this.items[tab.id];
35730         tab.fireEvent("close", tab);
35731         tab.purgeListeners();
35732         this.autoSizeTabs();
35733     },
35734
35735     getNextAvailable : function(start){
35736         var items = this.items;
35737         var index = start;
35738         // look for a next tab that will slide over to
35739         // replace the one being removed
35740         while(index < items.length){
35741             var item = items[++index];
35742             if(item && !item.isHidden()){
35743                 return item;
35744             }
35745         }
35746         // if one isn't found select the previous tab (on the left)
35747         index = start;
35748         while(index >= 0){
35749             var item = items[--index];
35750             if(item && !item.isHidden()){
35751                 return item;
35752             }
35753         }
35754         return null;
35755     },
35756
35757     /**
35758      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
35759      * @param {String/Number} id The id or index of the TabPanelItem to disable.
35760      */
35761     disableTab : function(id){
35762         var tab = this.items[id];
35763         if(tab && this.active != tab){
35764             tab.disable();
35765         }
35766     },
35767
35768     /**
35769      * Enables a {@link Roo.TabPanelItem} that is disabled.
35770      * @param {String/Number} id The id or index of the TabPanelItem to enable.
35771      */
35772     enableTab : function(id){
35773         var tab = this.items[id];
35774         tab.enable();
35775     },
35776
35777     /**
35778      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
35779      * @param {String/Number} id The id or index of the TabPanelItem to activate.
35780      * @return {Roo.TabPanelItem} The TabPanelItem.
35781      */
35782     activate : function(id){
35783         var tab = this.items[id];
35784         if(!tab){
35785             return null;
35786         }
35787         if(tab == this.active || tab.disabled){
35788             return tab;
35789         }
35790         var e = {};
35791         this.fireEvent("beforetabchange", this, e, tab);
35792         if(e.cancel !== true && !tab.disabled){
35793             if(this.active){
35794                 this.active.hide();
35795             }
35796             this.active = this.items[id];
35797             this.active.show();
35798             this.fireEvent("tabchange", this, this.active);
35799         }
35800         return tab;
35801     },
35802
35803     /**
35804      * Gets the active {@link Roo.TabPanelItem}.
35805      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
35806      */
35807     getActiveTab : function(){
35808         return this.active;
35809     },
35810
35811     /**
35812      * Updates the tab body element to fit the height of the container element
35813      * for overflow scrolling
35814      * @param {Number} targetHeight (optional) Override the starting height from the elements height
35815      */
35816     syncHeight : function(targetHeight){
35817         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35818         var bm = this.bodyEl.getMargins();
35819         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
35820         this.bodyEl.setHeight(newHeight);
35821         return newHeight;
35822     },
35823
35824     onResize : function(){
35825         if(this.monitorResize){
35826             this.autoSizeTabs();
35827         }
35828     },
35829
35830     /**
35831      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
35832      */
35833     beginUpdate : function(){
35834         this.updating = true;
35835     },
35836
35837     /**
35838      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
35839      */
35840     endUpdate : function(){
35841         this.updating = false;
35842         this.autoSizeTabs();
35843     },
35844
35845     /**
35846      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
35847      */
35848     autoSizeTabs : function(){
35849         var count = this.items.length;
35850         var vcount = count - this.hiddenCount;
35851         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
35852             return;
35853         }
35854         var w = Math.max(this.el.getWidth() - this.cpad, 10);
35855         var availWidth = Math.floor(w / vcount);
35856         var b = this.stripBody;
35857         if(b.getWidth() > w){
35858             var tabs = this.items;
35859             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
35860             if(availWidth < this.minTabWidth){
35861                 /*if(!this.sleft){    // incomplete scrolling code
35862                     this.createScrollButtons();
35863                 }
35864                 this.showScroll();
35865                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
35866             }
35867         }else{
35868             if(this.currentTabWidth < this.preferredTabWidth){
35869                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
35870             }
35871         }
35872     },
35873
35874     /**
35875      * Returns the number of tabs in this TabPanel.
35876      * @return {Number}
35877      */
35878      getCount : function(){
35879          return this.items.length;
35880      },
35881
35882     /**
35883      * Resizes all the tabs to the passed width
35884      * @param {Number} The new width
35885      */
35886     setTabWidth : function(width){
35887         this.currentTabWidth = width;
35888         for(var i = 0, len = this.items.length; i < len; i++) {
35889                 if(!this.items[i].isHidden()) {
35890                 this.items[i].setWidth(width);
35891             }
35892         }
35893     },
35894
35895     /**
35896      * Destroys this TabPanel
35897      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
35898      */
35899     destroy : function(removeEl){
35900         Roo.EventManager.removeResizeListener(this.onResize, this);
35901         for(var i = 0, len = this.items.length; i < len; i++){
35902             this.items[i].purgeListeners();
35903         }
35904         if(removeEl === true){
35905             this.el.update("");
35906             this.el.remove();
35907         }
35908     },
35909     
35910     createStrip : function(container)
35911     {
35912         var strip = document.createElement("nav");
35913         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
35914         container.appendChild(strip);
35915         return strip;
35916     },
35917     
35918     createStripList : function(strip)
35919     {
35920         // div wrapper for retard IE
35921         // returns the "tr" element.
35922         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
35923         //'<div class="x-tabs-strip-wrap">'+
35924           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
35925           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
35926         return strip.firstChild; //.firstChild.firstChild.firstChild;
35927     },
35928     createBody : function(container)
35929     {
35930         var body = document.createElement("div");
35931         Roo.id(body, "tab-body");
35932         //Roo.fly(body).addClass("x-tabs-body");
35933         Roo.fly(body).addClass("tab-content");
35934         container.appendChild(body);
35935         return body;
35936     },
35937     createItemBody :function(bodyEl, id){
35938         var body = Roo.getDom(id);
35939         if(!body){
35940             body = document.createElement("div");
35941             body.id = id;
35942         }
35943         //Roo.fly(body).addClass("x-tabs-item-body");
35944         Roo.fly(body).addClass("tab-pane");
35945          bodyEl.insertBefore(body, bodyEl.firstChild);
35946         return body;
35947     },
35948     /** @private */
35949     createStripElements :  function(stripEl, text, closable, tpl)
35950     {
35951         var td = document.createElement("li"); // was td..
35952         
35953         
35954         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
35955         
35956         
35957         stripEl.appendChild(td);
35958         /*if(closable){
35959             td.className = "x-tabs-closable";
35960             if(!this.closeTpl){
35961                 this.closeTpl = new Roo.Template(
35962                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35963                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
35964                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
35965                 );
35966             }
35967             var el = this.closeTpl.overwrite(td, {"text": text});
35968             var close = el.getElementsByTagName("div")[0];
35969             var inner = el.getElementsByTagName("em")[0];
35970             return {"el": el, "close": close, "inner": inner};
35971         } else {
35972         */
35973         // not sure what this is..
35974 //            if(!this.tabTpl){
35975                 //this.tabTpl = new Roo.Template(
35976                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35977                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
35978                 //);
35979 //                this.tabTpl = new Roo.Template(
35980 //                   '<a href="#">' +
35981 //                   '<span unselectable="on"' +
35982 //                            (this.disableTooltips ? '' : ' title="{text}"') +
35983 //                            ' >{text}</span></a>'
35984 //                );
35985 //                
35986 //            }
35987
35988
35989             var template = tpl || this.tabTpl || false;
35990             
35991             if(!template){
35992                 
35993                 template = new Roo.Template(
35994                    '<a href="#">' +
35995                    '<span unselectable="on"' +
35996                             (this.disableTooltips ? '' : ' title="{text}"') +
35997                             ' >{text}</span></a>'
35998                 );
35999             }
36000             
36001             switch (typeof(template)) {
36002                 case 'object' :
36003                     break;
36004                 case 'string' :
36005                     template = new Roo.Template(template);
36006                     break;
36007                 default :
36008                     break;
36009             }
36010             
36011             var el = template.overwrite(td, {"text": text});
36012             
36013             var inner = el.getElementsByTagName("span")[0];
36014             
36015             return {"el": el, "inner": inner};
36016             
36017     }
36018         
36019     
36020 });
36021
36022 /**
36023  * @class Roo.TabPanelItem
36024  * @extends Roo.util.Observable
36025  * Represents an individual item (tab plus body) in a TabPanel.
36026  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
36027  * @param {String} id The id of this TabPanelItem
36028  * @param {String} text The text for the tab of this TabPanelItem
36029  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
36030  */
36031 Roo.bootstrap.panel.TabItem = function(config){
36032     /**
36033      * The {@link Roo.TabPanel} this TabPanelItem belongs to
36034      * @type Roo.TabPanel
36035      */
36036     this.tabPanel = config.panel;
36037     /**
36038      * The id for this TabPanelItem
36039      * @type String
36040      */
36041     this.id = config.id;
36042     /** @private */
36043     this.disabled = false;
36044     /** @private */
36045     this.text = config.text;
36046     /** @private */
36047     this.loaded = false;
36048     this.closable = config.closable;
36049
36050     /**
36051      * The body element for this TabPanelItem.
36052      * @type Roo.Element
36053      */
36054     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
36055     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
36056     this.bodyEl.setStyle("display", "block");
36057     this.bodyEl.setStyle("zoom", "1");
36058     //this.hideAction();
36059
36060     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
36061     /** @private */
36062     this.el = Roo.get(els.el);
36063     this.inner = Roo.get(els.inner, true);
36064     this.textEl = Roo.get(this.el.dom.firstChild, true);
36065     this.pnode = Roo.get(els.el.parentNode, true);
36066     this.el.on("mousedown", this.onTabMouseDown, this);
36067     this.el.on("click", this.onTabClick, this);
36068     /** @private */
36069     if(config.closable){
36070         var c = Roo.get(els.close, true);
36071         c.dom.title = this.closeText;
36072         c.addClassOnOver("close-over");
36073         c.on("click", this.closeClick, this);
36074      }
36075
36076     this.addEvents({
36077          /**
36078          * @event activate
36079          * Fires when this tab becomes the active tab.
36080          * @param {Roo.TabPanel} tabPanel The parent TabPanel
36081          * @param {Roo.TabPanelItem} this
36082          */
36083         "activate": true,
36084         /**
36085          * @event beforeclose
36086          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
36087          * @param {Roo.TabPanelItem} this
36088          * @param {Object} e Set cancel to true on this object to cancel the close.
36089          */
36090         "beforeclose": true,
36091         /**
36092          * @event close
36093          * Fires when this tab is closed.
36094          * @param {Roo.TabPanelItem} this
36095          */
36096          "close": true,
36097         /**
36098          * @event deactivate
36099          * Fires when this tab is no longer the active tab.
36100          * @param {Roo.TabPanel} tabPanel The parent TabPanel
36101          * @param {Roo.TabPanelItem} this
36102          */
36103          "deactivate" : true
36104     });
36105     this.hidden = false;
36106
36107     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
36108 };
36109
36110 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
36111            {
36112     purgeListeners : function(){
36113        Roo.util.Observable.prototype.purgeListeners.call(this);
36114        this.el.removeAllListeners();
36115     },
36116     /**
36117      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
36118      */
36119     show : function(){
36120         this.pnode.addClass("active");
36121         this.showAction();
36122         if(Roo.isOpera){
36123             this.tabPanel.stripWrap.repaint();
36124         }
36125         this.fireEvent("activate", this.tabPanel, this);
36126     },
36127
36128     /**
36129      * Returns true if this tab is the active tab.
36130      * @return {Boolean}
36131      */
36132     isActive : function(){
36133         return this.tabPanel.getActiveTab() == this;
36134     },
36135
36136     /**
36137      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
36138      */
36139     hide : function(){
36140         this.pnode.removeClass("active");
36141         this.hideAction();
36142         this.fireEvent("deactivate", this.tabPanel, this);
36143     },
36144
36145     hideAction : function(){
36146         this.bodyEl.hide();
36147         this.bodyEl.setStyle("position", "absolute");
36148         this.bodyEl.setLeft("-20000px");
36149         this.bodyEl.setTop("-20000px");
36150     },
36151
36152     showAction : function(){
36153         this.bodyEl.setStyle("position", "relative");
36154         this.bodyEl.setTop("");
36155         this.bodyEl.setLeft("");
36156         this.bodyEl.show();
36157     },
36158
36159     /**
36160      * Set the tooltip for the tab.
36161      * @param {String} tooltip The tab's tooltip
36162      */
36163     setTooltip : function(text){
36164         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
36165             this.textEl.dom.qtip = text;
36166             this.textEl.dom.removeAttribute('title');
36167         }else{
36168             this.textEl.dom.title = text;
36169         }
36170     },
36171
36172     onTabClick : function(e){
36173         e.preventDefault();
36174         this.tabPanel.activate(this.id);
36175     },
36176
36177     onTabMouseDown : function(e){
36178         e.preventDefault();
36179         this.tabPanel.activate(this.id);
36180     },
36181 /*
36182     getWidth : function(){
36183         return this.inner.getWidth();
36184     },
36185
36186     setWidth : function(width){
36187         var iwidth = width - this.pnode.getPadding("lr");
36188         this.inner.setWidth(iwidth);
36189         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
36190         this.pnode.setWidth(width);
36191     },
36192 */
36193     /**
36194      * Show or hide the tab
36195      * @param {Boolean} hidden True to hide or false to show.
36196      */
36197     setHidden : function(hidden){
36198         this.hidden = hidden;
36199         this.pnode.setStyle("display", hidden ? "none" : "");
36200     },
36201
36202     /**
36203      * Returns true if this tab is "hidden"
36204      * @return {Boolean}
36205      */
36206     isHidden : function(){
36207         return this.hidden;
36208     },
36209
36210     /**
36211      * Returns the text for this tab
36212      * @return {String}
36213      */
36214     getText : function(){
36215         return this.text;
36216     },
36217     /*
36218     autoSize : function(){
36219         //this.el.beginMeasure();
36220         this.textEl.setWidth(1);
36221         /*
36222          *  #2804 [new] Tabs in Roojs
36223          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
36224          */
36225         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
36226         //this.el.endMeasure();
36227     //},
36228
36229     /**
36230      * Sets the text for the tab (Note: this also sets the tooltip text)
36231      * @param {String} text The tab's text and tooltip
36232      */
36233     setText : function(text){
36234         this.text = text;
36235         this.textEl.update(text);
36236         this.setTooltip(text);
36237         //if(!this.tabPanel.resizeTabs){
36238         //    this.autoSize();
36239         //}
36240     },
36241     /**
36242      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
36243      */
36244     activate : function(){
36245         this.tabPanel.activate(this.id);
36246     },
36247
36248     /**
36249      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
36250      */
36251     disable : function(){
36252         if(this.tabPanel.active != this){
36253             this.disabled = true;
36254             this.pnode.addClass("disabled");
36255         }
36256     },
36257
36258     /**
36259      * Enables this TabPanelItem if it was previously disabled.
36260      */
36261     enable : function(){
36262         this.disabled = false;
36263         this.pnode.removeClass("disabled");
36264     },
36265
36266     /**
36267      * Sets the content for this TabPanelItem.
36268      * @param {String} content The content
36269      * @param {Boolean} loadScripts true to look for and load scripts
36270      */
36271     setContent : function(content, loadScripts){
36272         this.bodyEl.update(content, loadScripts);
36273     },
36274
36275     /**
36276      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
36277      * @return {Roo.UpdateManager} The UpdateManager
36278      */
36279     getUpdateManager : function(){
36280         return this.bodyEl.getUpdateManager();
36281     },
36282
36283     /**
36284      * Set a URL to be used to load the content for this TabPanelItem.
36285      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
36286      * @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)
36287      * @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)
36288      * @return {Roo.UpdateManager} The UpdateManager
36289      */
36290     setUrl : function(url, params, loadOnce){
36291         if(this.refreshDelegate){
36292             this.un('activate', this.refreshDelegate);
36293         }
36294         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36295         this.on("activate", this.refreshDelegate);
36296         return this.bodyEl.getUpdateManager();
36297     },
36298
36299     /** @private */
36300     _handleRefresh : function(url, params, loadOnce){
36301         if(!loadOnce || !this.loaded){
36302             var updater = this.bodyEl.getUpdateManager();
36303             updater.update(url, params, this._setLoaded.createDelegate(this));
36304         }
36305     },
36306
36307     /**
36308      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
36309      *   Will fail silently if the setUrl method has not been called.
36310      *   This does not activate the panel, just updates its content.
36311      */
36312     refresh : function(){
36313         if(this.refreshDelegate){
36314            this.loaded = false;
36315            this.refreshDelegate();
36316         }
36317     },
36318
36319     /** @private */
36320     _setLoaded : function(){
36321         this.loaded = true;
36322     },
36323
36324     /** @private */
36325     closeClick : function(e){
36326         var o = {};
36327         e.stopEvent();
36328         this.fireEvent("beforeclose", this, o);
36329         if(o.cancel !== true){
36330             this.tabPanel.removeTab(this.id);
36331         }
36332     },
36333     /**
36334      * The text displayed in the tooltip for the close icon.
36335      * @type String
36336      */
36337     closeText : "Close this tab"
36338 });