4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
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)
23 * Do not use directly - it does not do anything..
24 * @param {Object} config The config object
29 Roo.bootstrap.Component = function(config){
30 Roo.bootstrap.Component.superclass.constructor.call(this, config);
34 * @event childrenrendered
35 * Fires when the children have been rendered..
36 * @param {Roo.bootstrap.Component} this
38 "childrenrendered" : true
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
50 allowDomMove : false, // to stop relocations in parent onRender...
60 * Initialize Events for the element
62 initEvents : function() { },
68 can_build_overlaid : true,
70 container_method : false,
77 // returns the parent component..
78 return Roo.ComponentMgr.get(this.parentId)
84 onRender : function(ct, position)
86 // Roo.log("Call onRender: " + this.xtype);
88 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
91 if (this.el.attr('xtype')) {
92 this.el.attr('xtypex', this.el.attr('xtype'));
93 this.el.dom.removeAttribute('xtype');
103 var cfg = Roo.apply({}, this.getAutoCreate());
105 cfg.id = this.id || Roo.id();
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];
115 cfg.dataId = this.dataId;
119 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
122 if (this.style) { // fixme needs to support more complex style data.
123 cfg.style = this.style;
127 cfg.name = this.name;
130 this.el = ct.createChild(cfg, position);
133 this.tooltipEl().attr('tooltip', this.tooltip);
136 if(this.tabIndex !== undefined){
137 this.el.dom.setAttribute('tabIndex', this.tabIndex);
144 * Fetch the element to add children to
145 * @return {Roo.Element} defaults to this.el
147 getChildContainer : function()
152 * Fetch the element to display the tooltip on.
153 * @return {Roo.Element} defaults to this.el
155 tooltipEl : function()
160 addxtype : function(tree,cntr)
164 cn = Roo.factory(tree);
165 //Roo.log(['addxtype', cn]);
167 cn.parentType = this.xtype; //??
168 cn.parentId = this.id;
170 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171 if (typeof(cn.container_method) == 'string') {
172 cntr = cn.container_method;
176 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
178 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
180 var build_from_html = Roo.XComponent.build_from_html;
182 var is_body = (tree.xtype == 'Body') ;
184 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
186 var self_cntr_el = Roo.get(this[cntr](false));
188 // do not try and build conditional elements
189 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
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);
198 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
201 return this.addxtypeChild(Roo.apply({}, tree),cntr);
204 Roo.log('skipping render');
210 if (!build_from_html) {
214 // this i think handles overlaying multiple children of the same type
215 // with the sam eelement.. - which might be buggy..
217 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
223 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
227 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
234 addxtypeChild : function (tree, cntr, is_body)
236 Roo.debug && Roo.log('addxtypeChild:' + cntr);
238 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
241 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242 (typeof(tree['flexy:foreach']) != 'undefined');
246 skip_children = false;
247 // render the element if it's not BODY.
250 cn = Roo.factory(tree);
252 cn.parentType = this.xtype; //??
253 cn.parentId = this.id;
255 var build_from_html = Roo.XComponent.build_from_html;
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') {
264 var self_cntr_el = Roo.get(this[cntr](false));
265 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
267 //Roo.log(Roo.XComponent.build_from_html);
268 //Roo.log("got echild:");
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.
276 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
283 //echild.dom.removeAttribute('xtype');
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);
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);
300 Roo.debug && Roo.log('skipping all children');
301 skip_children = true;
306 // actually if flexy:foreach is found, we really want to create
307 // multiple copies here...
309 //Roo.log(this[cntr]());
310 // some elements do not have render methods.. like the layouts...
311 cn.render && cn.render(this[cntr](true));
313 // then add the element..
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)));
328 if (!tree.items || !tree.items.length) {
330 //Roo.log(["no children", this]);
335 var items = tree.items;
338 //Roo.log(items.length);
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])));
349 //Roo.log("fire childrenrendered");
351 cn.fireEvent('childrenrendered', this);
356 * Show a component - removes 'hidden' class
361 this.el.removeClass('hidden');
365 * Hide a component - adds 'hidden' class
369 if (this.el && !this.el.hasClass('hidden')) {
370 this.el.addClass('hidden');
384 * @class Roo.bootstrap.Body
385 * @extends Roo.bootstrap.Component
386 * Bootstrap Body class
390 * @param {Object} config The config object
393 Roo.bootstrap.Body = function(config){
395 config = config || {};
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);
404 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
406 is_body : true,// just to make sure it's constructed?
411 onRender : function(ct, position)
413 /* Roo.log("Roo.bootstrap.Body - onRender");
414 if (this.cls && this.cls.length) {
415 Roo.get(document.body).addClass(this.cls);
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
446 * @param {Object} config The config object
449 Roo.bootstrap.ButtonGroup = function(config){
450 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
453 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
461 getAutoCreate : function(){
467 cfg.html = this.html || cfg.html;
478 if (['vertical','justified'].indexOf(this.align)!==-1) {
479 cfg.cls = 'btn-group-' + this.align;
481 if (this.align == 'justified') {
482 console.log(this.items);
486 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
487 cfg.cls += ' btn-group-' + this.size;
490 if (this.direction == 'up') {
491 cfg.cls += ' dropup' ;
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)
530 * Create a new button
531 * @param {Object} config The config object
535 Roo.bootstrap.Button = function(config){
536 Roo.bootstrap.Button.superclass.constructor.call(this, config);
541 * When a butotn is pressed
542 * @param {Roo.bootstrap.Button} this
543 * @param {Roo.EventObject} e
548 * After the button has been toggles
549 * @param {Roo.EventObject} e
550 * @param {boolean} pressed (also available as button.pressed)
556 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
574 preventDefault: true,
583 getAutoCreate : function(){
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.";
597 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
599 if (this.toggle == true) {
602 cls: 'slider-frame roo-button',
607 'data-off-text':'OFF',
608 cls: 'slider-button',
614 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
615 cfg.cls += ' '+this.weight;
624 cfg["aria-hidden"] = true;
626 cfg.html = "×";
632 if (this.theme==='default') {
633 cfg.cls = 'btn roo-button';
635 //if (this.parentType != 'Navbar') {
636 this.weight = this.weight.length ? this.weight : 'default';
638 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
640 cfg.cls += ' btn-' + this.weight;
642 } else if (this.theme==='glow') {
645 cfg.cls = 'btn-glow roo-button';
647 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
649 cfg.cls += ' ' + this.weight;
655 this.cls += ' inverse';
660 cfg.cls += ' active';
664 cfg.disabled = 'disabled';
668 Roo.log('changing to ul' );
670 this.glyphicon = 'caret';
673 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
675 //gsRoo.log(this.parentType);
676 if (this.parentType === 'Navbar' && !this.parent().bar) {
677 Roo.log('changing to li?');
686 href : this.href || '#'
689 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
690 cfg.cls += ' dropdown';
697 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
699 if (this.glyphicon) {
700 cfg.html = ' ' + cfg.html;
705 cls: 'glyphicon glyphicon-' + this.glyphicon
715 // cfg.cls='btn roo-button';
719 var value = cfg.html;
724 cls: 'glyphicon glyphicon-' + this.glyphicon,
743 cfg.cls += ' dropdown';
744 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
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;
753 if(this.removeClass){
758 cfg.target = this.target;
763 initEvents: function() {
764 // Roo.log('init events?');
765 // Roo.log(this.el.dom);
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));
775 if (this.el.hasClass('roo-button')) {
776 this.el.on('click', this.onClick, this);
778 this.el.select('.roo-button').on('click', this.onClick, this);
781 if(this.removeClass){
782 this.el.on('click', this.onClick, this);
785 this.el.enableDisplayMode();
788 onClick : function(e)
795 Roo.log('button on click ');
796 if(this.preventDefault){
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);
806 this.fireEvent('click', this, e);
810 * Enables this button
814 this.disabled = false;
815 this.el.removeClass('disabled');
819 * Disable this button
823 this.disabled = true;
824 this.el.addClass('disabled');
827 * sets the active state on/off,
828 * @param {Boolean} state (optional) Force a particular state
830 setActive : function(v) {
832 this.el[v ? 'addClass' : 'removeClass']('active');
835 * toggles the current active state
837 toggleActive : function()
839 var active = this.el.hasClass('active');
840 this.setActive(!active);
844 setText : function(str)
846 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
850 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
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
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
891 * @cfg {String} icon (info-sign|check|...) glyphicon name
893 * @cfg {String} html content of column.
896 * Create a new Column
897 * @param {Object} config The config object
900 Roo.bootstrap.Column = function(config){
901 Roo.bootstrap.Column.superclass.constructor.call(this, config);
904 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
922 getAutoCreate : function(){
923 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
931 ['xs','sm','md','lg'].map(function(size){
932 //Roo.log( size + ':' + settings[size]);
934 if (settings[size+'off'] !== false) {
935 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
938 if (settings[size] === false) {
942 if (!settings[size]) { // 0 = hidden
943 cfg.cls += ' hidden-' + size;
946 cfg.cls += ' col-' + size + '-' + settings[size];
951 cfg.cls += ' hidden';
954 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
955 cfg.cls +=' alert alert-' + this.alert;
959 if (this.html.length) {
960 cfg.html = this.html;
964 if (this.fasize > 1) {
965 fasize = ' fa-' + this.fasize + 'x';
967 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
972 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
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
1013 * Create a new Container
1014 * @param {Object} config The config object
1017 Roo.bootstrap.Container = function(config){
1018 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1024 * After the panel has been expand
1026 * @param {Roo.bootstrap.Container} this
1031 * After the panel has been collapsed
1033 * @param {Roo.bootstrap.Container} this
1038 * When a element is chick
1039 * @param {Roo.bootstrap.Container} this
1040 * @param {Roo.EventObject} e
1046 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1064 getChildContainer : function() {
1070 if (this.panel.length) {
1071 return this.el.select('.panel-body',true).first();
1078 getAutoCreate : function(){
1081 tag : this.tag || 'div',
1085 if (this.jumbotron) {
1086 cfg.cls = 'jumbotron';
1091 // - this is applied by the parent..
1093 // cfg.cls = this.cls + '';
1096 if (this.sticky.length) {
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%');
1104 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1108 if (this.well.length) {
1109 switch (this.well) {
1112 cfg.cls +=' well well-' +this.well;
1121 cfg.cls += ' hidden';
1125 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1126 cfg.cls +=' alert alert-' + this.alert;
1131 if (this.panel.length) {
1132 cfg.cls += ' panel panel-' + this.panel;
1134 if (this.header.length) {
1138 if(this.expandable){
1140 cfg.cls = cfg.cls + ' expandable';
1144 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1152 cls : 'panel-title',
1153 html : (this.expandable ? ' ' : '') + this.header
1157 cls: 'panel-header-right',
1163 cls : 'panel-heading',
1164 style : this.expandable ? 'cursor: pointer' : '',
1172 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1177 if (this.footer.length) {
1179 cls : 'panel-footer',
1188 body.html = this.html || cfg.html;
1189 // prefix with the icons..
1191 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1194 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1199 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1200 cfg.cls = 'container';
1206 initEvents: function()
1208 if(this.expandable){
1209 var headerEl = this.headerEl();
1212 headerEl.on('click', this.onToggleClick, this);
1217 this.el.on('click', this.onClick, this);
1222 onToggleClick : function()
1224 var headerEl = this.headerEl();
1240 if(this.fireEvent('expand', this)) {
1242 this.expanded = true;
1244 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1246 this.el.select('.panel-body',true).first().removeClass('hide');
1248 var toggleEl = this.toggleEl();
1254 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1259 collapse : function()
1261 if(this.fireEvent('collapse', this)) {
1263 this.expanded = false;
1265 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1266 this.el.select('.panel-body',true).first().addClass('hide');
1268 var toggleEl = this.toggleEl();
1274 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1278 toggleEl : function()
1280 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1284 return this.el.select('.panel-heading .fa',true).first();
1287 headerEl : function()
1289 if(!this.el || !this.panel.length || !this.header.length){
1293 return this.el.select('.panel-heading',true).first()
1296 titleEl : function()
1298 if(!this.el || !this.panel.length || !this.header.length){
1302 return this.el.select('.panel-title',true).first();
1305 setTitle : function(v)
1307 var titleEl = this.titleEl();
1313 titleEl.dom.innerHTML = v;
1316 getTitle : function()
1319 var titleEl = this.titleEl();
1325 return titleEl.dom.innerHTML;
1328 setRightTitle : function(v)
1330 var t = this.el.select('.panel-header-right',true).first();
1336 t.dom.innerHTML = v;
1339 onClick : function(e)
1343 this.fireEvent('click', this, e);
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
1372 * Create a new Input
1373 * @param {Object} config The config object
1376 Roo.bootstrap.Img = function(config){
1377 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1383 * The img click event for the img.
1384 * @param {Roo.EventObject} e
1390 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1392 imgResponsive: true,
1402 getAutoCreate : function()
1404 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1405 return this.createSingleImg();
1410 cls: 'roo-image-responsive-group',
1415 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1417 if(!_this[size + 'Url']){
1423 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1424 html: _this.html || cfg.html,
1425 src: _this[size + 'Url']
1428 img.cls += ' roo-image-responsive-' + size;
1430 var s = ['xs', 'sm', 'md', 'lg'];
1432 s.splice(s.indexOf(size), 1);
1434 Roo.each(s, function(ss){
1435 img.cls += ' hidden-' + ss;
1438 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1439 cfg.cls += ' img-' + _this.border;
1443 cfg.alt = _this.alt;
1456 a.target = _this.target;
1460 cfg.cn.push((_this.href) ? a : img);
1467 createSingleImg : function()
1471 cls: (this.imgResponsive) ? 'img-responsive' : '',
1473 src : 'about:blank' // just incase src get's set to undefined?!?
1476 cfg.html = this.html || cfg.html;
1478 cfg.src = this.src || cfg.src;
1480 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1481 cfg.cls += ' img-' + this.border;
1498 a.target = this.target;
1503 return (this.href) ? a : cfg;
1506 initEvents: function()
1509 this.el.on('click', this.onClick, this);
1514 onClick : function(e)
1516 Roo.log('img onclick');
1517 this.fireEvent('click', this, e);
1520 * Sets the url of the image - used to update it
1521 * @param {String} url the url of the image
1524 setSrc : function(url)
1528 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1529 this.el.dom.src = url;
1533 this.el.select('img', true).first().dom.src = url;
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
1559 * @cfg {Boolean} preventDefault (true | false) default false
1563 * Create a new Input
1564 * @param {Object} config The config object
1567 Roo.bootstrap.Link = function(config){
1568 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1574 * The img click event for the img.
1575 * @param {Roo.EventObject} e
1581 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1585 preventDefault: false,
1591 getAutoCreate : function()
1593 var html = this.html || '';
1595 if (this.fa !== false) {
1596 html = '<i class="fa fa-' + this.fa + '"></i>';
1601 // anchor's do not require html/href...
1602 if (this.anchor === false) {
1604 cfg.href = this.href || '#';
1606 cfg.name = this.anchor;
1607 if (this.html !== false || this.fa !== false) {
1610 if (this.href !== false) {
1611 cfg.href = this.href;
1615 if(this.alt !== false){
1620 if(this.target !== false) {
1621 cfg.target = this.target;
1627 initEvents: function() {
1629 if(!this.href || this.preventDefault){
1630 this.el.on('click', this.onClick, this);
1634 onClick : function(e)
1636 if(this.preventDefault){
1639 //Roo.log('img onclick');
1640 this.fireEvent('click', this, e);
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
1660 * Create a new Header
1661 * @param {Object} config The config object
1665 Roo.bootstrap.Header = function(config){
1666 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1669 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1677 getAutoCreate : function(){
1682 tag: 'h' + (1 *this.level),
1683 html: this.html || ''
1695 * Ext JS Library 1.1.1
1696 * Copyright(c) 2006-2007, Ext JS, LLC.
1698 * Originally Released Under LGPL - original licence link has changed is not relivant.
1701 * <script type="text/javascript">
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.
1709 Roo.bootstrap.MenuMgr = function(){
1710 var menus, active, groups = {}, attached = false, lastShow = new Date();
1712 // private - called when first menu is created
1715 active = new Roo.util.MixedCollection();
1716 Roo.get(document).addKeyListener(27, function(){
1717 if(active.length > 0){
1725 if(active && active.length > 0){
1726 var c = active.clone();
1736 if(active.length < 1){
1737 Roo.get(document).un("mouseup", onMouseDown);
1745 var last = active.last();
1746 lastShow = new Date();
1749 Roo.get(document).on("mouseup", onMouseDown);
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);
1762 function onBeforeHide(m){
1764 m.activeChild.hide();
1766 if(m.autoHideTimer){
1767 clearTimeout(m.autoHideTimer);
1768 delete m.autoHideTimer;
1773 function onBeforeShow(m){
1774 var pm = m.parentMenu;
1775 if(!pm && !m.allowOtherMenus){
1777 }else if(pm && pm.activeChild && active != m){
1778 pm.activeChild.hide();
1782 // private this should really trigger on mouseup..
1783 function onMouseDown(e){
1784 Roo.log("on Mouse Up");
1786 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1787 Roo.log("MenuManager hideAll");
1796 function onBeforeCheck(mi, state){
1798 var g = groups[mi.group];
1799 for(var i = 0, l = g.length; i < l; i++){
1801 g[i].setChecked(false);
1810 * Hides all menus that are currently visible
1812 hideAll : function(){
1817 register : function(menu){
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);
1827 if(g && menu.events["checkchange"]){
1831 groups[g].push(menu);
1832 menu.on("checkchange", onCheck);
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.
1841 get : function(menu){
1842 if(typeof menu == "string"){ // menu id
1844 }else if(menu.events){ // menu instance
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);
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);
1864 if(g && menu.events["checkchange"]){
1865 groups[g].remove(menu);
1866 menu.un("checkchange", onCheck);
1871 registerCheckable : function(menuItem){
1872 var g = menuItem.group;
1877 groups[g].push(menuItem);
1878 menuItem.on("beforecheckchange", onBeforeCheck);
1883 unregisterCheckable : function(menuItem){
1884 var g = menuItem.group;
1886 groups[g].remove(menuItem);
1887 menuItem.un("beforecheckchange", onBeforeCheck);
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)
1909 * @param {Object} config The config object
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);
1921 * Fires before this menu is displayed
1922 * @param {Roo.menu.Menu} this
1927 * Fires before this menu is hidden
1928 * @param {Roo.menu.Menu} this
1933 * Fires after this menu is displayed
1934 * @param {Roo.menu.Menu} this
1939 * Fires after this menu is hidden
1940 * @param {Roo.menu.Menu} this
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
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
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
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
1975 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1978 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1982 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1985 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1987 registerMenu : true,
1989 menuItems :false, // stores the menu items..
1999 getChildContainer : function() {
2003 getAutoCreate : function(){
2005 //if (['right'].indexOf(this.align)!==-1) {
2006 // cfg.cn[1].cls += ' pull-right'
2012 cls : 'dropdown-menu' ,
2013 style : 'z-index:1000'
2017 if (this.type === 'submenu') {
2018 cfg.cls = 'submenu active';
2020 if (this.type === 'treeview') {
2021 cfg.cls = 'treeview-menu';
2026 initEvents : function() {
2028 // Roo.log("ADD event");
2029 // Roo.log(this.triggerEl.dom);
2031 this.triggerEl.on('click', this.onTriggerClick, this);
2033 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2035 this.triggerEl.addClass('dropdown-toggle');
2038 this.el.on('touchstart' , this.onTouch, this);
2040 this.el.on('click' , this.onClick, this);
2042 this.el.on("mouseover", this.onMouseOver, this);
2043 this.el.on("mouseout", this.onMouseOut, this);
2047 findTargetItem : function(e)
2049 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2053 //Roo.log(t); Roo.log(t.id);
2055 //Roo.log(this.menuitems);
2056 return this.menuitems.get(t.id);
2058 //return this.items.get(t.menuItemId);
2064 onTouch : function(e)
2066 Roo.log("menu.onTouch");
2067 //e.stopEvent(); this make the user popdown broken
2071 onClick : function(e)
2073 Roo.log("menu.onClick");
2075 var t = this.findTargetItem(e);
2076 if(!t || t.isContainer){
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;
2088 this.setActiveItem(t, true);
2096 Roo.log('pass click event');
2100 this.fireEvent("click", this, t, e);
2104 (function() { _this.hide(); }).defer(100);
2107 onMouseOver : function(e){
2108 var t = this.findTargetItem(e);
2111 // if(t.canActivate && !t.disabled){
2112 // this.setActiveItem(t, true);
2116 this.fireEvent("mouseover", this, e, t);
2118 isVisible : function(){
2119 return !this.hidden;
2121 onMouseOut : function(e){
2122 var t = this.findTargetItem(e);
2125 // if(t == this.activeItem && t.shouldDeactivate(e)){
2126 // this.activeItem.deactivate();
2127 // delete this.activeItem;
2130 this.fireEvent("mouseout", this, e, t);
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)
2141 show : function(el, pos, parentMenu){
2142 this.parentMenu = parentMenu;
2146 this.fireEvent("beforeshow", this);
2147 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
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)
2154 showAt : function(xy, parentMenu, /* private: */_e){
2155 this.parentMenu = parentMenu;
2160 this.fireEvent("beforeshow", this);
2161 //xy = this.el.adjustForConstraints(xy);
2165 this.hideMenuItems();
2166 this.hidden = false;
2167 this.triggerEl.addClass('open');
2169 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2170 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2173 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2178 this.fireEvent("show", this);
2184 this.doFocus.defer(50, this);
2188 doFocus : function(){
2190 this.focusEl.focus();
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)
2198 hide : function(deep)
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;
2208 this.triggerEl.removeClass('open');;
2210 this.fireEvent("hide", this);
2212 if(deep === true && this.parentMenu){
2213 this.parentMenu.hide(true);
2217 onTriggerClick : function(e)
2219 Roo.log('trigger click');
2221 var target = e.getTarget();
2223 Roo.log(target.nodeName.toLowerCase());
2225 if(target.nodeName.toLowerCase() === 'i'){
2231 onTriggerPress : function(e)
2233 Roo.log('trigger press');
2234 //Roo.log(e.getTarget());
2235 // Roo.log(this.triggerEl.dom);
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?');
2244 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2248 if (this.isVisible()) {
2253 this.show(this.triggerEl, false, false);
2256 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2263 hideMenuItems : function()
2265 Roo.log("hide Menu Items");
2269 //$(backdrop).remove()
2270 this.el.select('.open',true).each(function(aa) {
2272 aa.removeClass('open');
2273 //var parent = getParent($(this))
2274 //var relatedTarget = { relatedTarget: this }
2276 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2277 //if (e.isDefaultPrevented()) return
2278 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2281 addxtypeChild : function (tree, cntr) {
2282 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2284 this.menuitems.add(comp);
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.
2318 * Create a new MenuItem
2319 * @param {Object} config The config object
2323 Roo.bootstrap.MenuItem = function(config){
2324 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2329 * The raw click event for the entire grid.
2330 * @param {Roo.bootstrap.MenuItem} this
2331 * @param {Roo.EventObject} e
2337 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2341 preventDefault: false,
2342 isContainer : false,
2346 getAutoCreate : function(){
2348 if(this.isContainer){
2351 cls: 'dropdown-menu-item'
2365 if (this.fa !== false) {
2368 cls : 'fa fa-' + this.fa
2377 cls: 'dropdown-menu-item',
2380 if (this.parent().type == 'treeview') {
2381 cfg.cls = 'treeview-menu';
2384 cfg.cls += ' active';
2389 anc.href = this.href || cfg.cn[0].href ;
2390 ctag.html = this.html || cfg.cn[0].html ;
2394 initEvents: function()
2396 if (this.parent().type == 'treeview') {
2397 this.el.select('a').on('click', this.onClick, this);
2400 this.menu.parentType = this.xtype;
2401 this.menu.triggerEl = this.el;
2402 this.menu = this.addxtype(Roo.apply({}, this.menu));
2406 onClick : function(e)
2408 Roo.log('item on click ');
2410 if(this.preventDefault){
2413 //this.parent().hideMenuItems();
2415 this.fireEvent('click', this, e);
2434 * @class Roo.bootstrap.MenuSeparator
2435 * @extends Roo.bootstrap.Component
2436 * Bootstrap MenuSeparator class
2439 * Create a new MenuItem
2440 * @param {Object} config The config object
2444 Roo.bootstrap.MenuSeparator = function(config){
2445 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2448 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2450 getAutoCreate : function(){
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
2485 * Create a new Modal Dialog
2486 * @param {Object} config The config object
2489 Roo.bootstrap.Modal = function(config){
2490 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2495 * The raw btnclick event for the button
2496 * @param {Roo.EventObject} e
2500 this.buttons = this.buttons || [];
2503 this.tmpl = Roo.factory(this.tmpl);
2508 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2510 title : 'test dialog',
2520 specificTitle: false,
2522 buttonPosition: 'right',
2541 onRender : function(ct, position)
2543 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2546 var cfg = Roo.apply({}, this.getAutoCreate());
2549 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2551 //if (!cfg.name.length) {
2555 cfg.cls += ' ' + this.cls;
2558 cfg.style = this.style;
2560 this.el = Roo.get(document.body).createChild(cfg, position);
2562 //var type = this.el.dom.type;
2565 if(this.tabIndex !== undefined){
2566 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2569 this.dialogEl = this.el.select('.modal-dialog',true).first();
2570 this.bodyEl = this.el.select('.modal-body',true).first();
2571 this.closeEl = this.el.select('.modal-header .close', true).first();
2572 this.headerEl = this.el.select('.modal-header',true).first();
2573 this.titleEl = this.el.select('.modal-title',true).first();
2574 this.footerEl = this.el.select('.modal-footer',true).first();
2576 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2577 this.maskEl.enableDisplayMode("block");
2579 //this.el.addClass("x-dlg-modal");
2581 if (this.buttons.length) {
2582 Roo.each(this.buttons, function(bb) {
2583 var b = Roo.apply({}, bb);
2584 b.xns = b.xns || Roo.bootstrap;
2585 b.xtype = b.xtype || 'Button';
2586 if (typeof(b.listeners) == 'undefined') {
2587 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2590 var btn = Roo.factory(b);
2592 btn.render(this.el.select('.modal-footer div').first());
2596 // render the children.
2599 if(typeof(this.items) != 'undefined'){
2600 var items = this.items;
2603 for(var i =0;i < items.length;i++) {
2604 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2608 this.items = nitems;
2610 // where are these used - they used to be body/close/footer
2614 //this.el.addClass([this.fieldClass, this.cls]);
2618 getAutoCreate : function(){
2623 html : this.html || ''
2628 cls : 'modal-title',
2632 if(this.specificTitle){
2638 if (this.allow_close) {
2650 if(this.size.length){
2651 size = 'modal-' + this.size;
2656 style : 'display: none',
2659 cls: "modal-dialog " + size,
2662 cls : "modal-content",
2665 cls : 'modal-header',
2670 cls : 'modal-footer',
2674 cls: 'btn-' + this.buttonPosition
2691 modal.cls += ' fade';
2697 getChildContainer : function() {
2702 getButtonContainer : function() {
2703 return this.el.select('.modal-footer div',true).first();
2706 initEvents : function()
2708 if (this.allow_close) {
2709 this.closeEl.on('click', this.hide, this);
2711 Roo.EventManager.onWindowResize(this.resize, this, true);
2718 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2719 if (this.fitwindow) {
2720 var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2721 var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2726 setSize : function(w,h)
2736 if (!this.rendered) {
2740 this.el.setStyle('display', 'block');
2742 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2745 this.el.addClass('in');
2748 this.el.addClass('in');
2752 // not sure how we can show data in here..
2754 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2757 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.setStyle('zIndex', Roo.bootstrap.Modal.zIndex++);
2762 this.el.setStyle('zIndex', Roo.bootstrap.Modal.zIndex++);
2764 this.fireEvent('show', this);
2769 this.items.forEach( function(e) {
2770 e.layout ? e.layout() : false;
2778 if(this.fireEvent("beforehide", this) !== false){
2780 Roo.get(document.body).removeClass("x-body-masked");
2781 this.el.removeClass('in');
2782 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2784 if(this.animate){ // why
2786 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2788 this.el.setStyle('display', 'none');
2790 this.fireEvent('hide', this);
2794 addButton : function(str, cb)
2798 var b = Roo.apply({}, { html : str } );
2799 b.xns = b.xns || Roo.bootstrap;
2800 b.xtype = b.xtype || 'Button';
2801 if (typeof(b.listeners) == 'undefined') {
2802 b.listeners = { click : cb.createDelegate(this) };
2805 var btn = Roo.factory(b);
2807 btn.render(this.el.select('.modal-footer div').first());
2813 setDefaultButton : function(btn)
2815 //this.el.select('.modal-footer').()
2819 resizeTo: function(w,h)
2823 this.dialogEl.setWidth(w);
2824 if (this.diff === false) {
2825 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2828 this.bodyEl.setHeight(h-this.diff);
2832 setContentSize : function(w, h)
2836 onButtonClick: function(btn,e)
2839 this.fireEvent('btnclick', btn.name, e);
2842 * Set the title of the Dialog
2843 * @param {String} str new Title
2845 setTitle: function(str) {
2846 this.titleEl.dom.innerHTML = str;
2849 * Set the body of the Dialog
2850 * @param {String} str new Title
2852 setBody: function(str) {
2853 this.bodyEl.dom.innerHTML = str;
2856 * Set the body of the Dialog using the template
2857 * @param {Obj} data - apply this data to the template and replace the body contents.
2859 applyBody: function(obj)
2862 Roo.log("Error - using apply Body without a template");
2865 this.tmpl.overwrite(this.bodyEl, obj);
2871 Roo.apply(Roo.bootstrap.Modal, {
2873 * Button config that displays a single OK button
2882 * Button config that displays Yes and No buttons
2898 * Button config that displays OK and Cancel buttons
2913 * Button config that displays Yes, No and Cancel buttons
2937 * messagebox - can be used as a replace
2941 * @class Roo.MessageBox
2942 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2946 Roo.Msg.alert('Status', 'Changes saved successfully.');
2948 // Prompt for user data:
2949 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2951 // process text value...
2955 // Show a dialog using config options:
2957 title:'Save Changes?',
2958 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2959 buttons: Roo.Msg.YESNOCANCEL,
2966 Roo.bootstrap.MessageBox = function(){
2967 var dlg, opt, mask, waitTimer;
2968 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2969 var buttons, activeTextEl, bwidth;
2973 var handleButton = function(button){
2975 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2979 var handleHide = function(){
2981 dlg.el.removeClass(opt.cls);
2984 // Roo.TaskMgr.stop(waitTimer);
2985 // waitTimer = null;
2990 var updateButtons = function(b){
2993 buttons["ok"].hide();
2994 buttons["cancel"].hide();
2995 buttons["yes"].hide();
2996 buttons["no"].hide();
2997 //dlg.footer.dom.style.display = 'none';
3000 dlg.footerEl.dom.style.display = '';
3001 for(var k in buttons){
3002 if(typeof buttons[k] != "function"){
3005 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3006 width += buttons[k].el.getWidth()+15;
3016 var handleEsc = function(d, k, e){
3017 if(opt && opt.closable !== false){
3027 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3028 * @return {Roo.BasicDialog} The BasicDialog element
3030 getDialog : function(){
3032 dlg = new Roo.bootstrap.Modal( {
3035 //constraintoviewport:false,
3037 //collapsible : false,
3042 //buttonAlign:"center",
3043 closeClick : function(){
3044 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3047 handleButton("cancel");
3052 dlg.on("hide", handleHide);
3054 //dlg.addKeyListener(27, handleEsc);
3056 this.buttons = buttons;
3057 var bt = this.buttonText;
3058 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3059 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3060 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3061 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3063 bodyEl = dlg.bodyEl.createChild({
3065 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3066 '<textarea class="roo-mb-textarea"></textarea>' +
3067 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3069 msgEl = bodyEl.dom.firstChild;
3070 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3071 textboxEl.enableDisplayMode();
3072 textboxEl.addKeyListener([10,13], function(){
3073 if(dlg.isVisible() && opt && opt.buttons){
3076 }else if(opt.buttons.yes){
3077 handleButton("yes");
3081 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3082 textareaEl.enableDisplayMode();
3083 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3084 progressEl.enableDisplayMode();
3086 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3087 //var pf = progressEl.dom.firstChild;
3089 //pp = Roo.get(pf.firstChild);
3090 //pp.setHeight(pf.offsetHeight);
3098 * Updates the message box body text
3099 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3100 * the XHTML-compliant non-breaking space character '&#160;')
3101 * @return {Roo.MessageBox} This message box
3103 updateText : function(text)
3105 if(!dlg.isVisible() && !opt.width){
3106 dlg.dialogEl.setWidth(this.maxWidth);
3107 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3109 msgEl.innerHTML = text || ' ';
3111 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3112 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3114 Math.min(opt.width || cw , this.maxWidth),
3115 Math.max(opt.minWidth || this.minWidth, bwidth)
3118 activeTextEl.setWidth(w);
3120 if(dlg.isVisible()){
3121 dlg.fixedcenter = false;
3123 // to big, make it scroll. = But as usual stupid IE does not support
3126 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3127 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3128 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3130 bodyEl.dom.style.height = '';
3131 bodyEl.dom.style.overflowY = '';
3134 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3136 bodyEl.dom.style.overflowX = '';
3139 dlg.setContentSize(w, bodyEl.getHeight());
3140 if(dlg.isVisible()){
3141 dlg.fixedcenter = true;
3147 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3148 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3149 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3150 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3151 * @return {Roo.MessageBox} This message box
3153 updateProgress : function(value, text){
3155 this.updateText(text);
3157 if (pp) { // weird bug on my firefox - for some reason this is not defined
3158 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3164 * Returns true if the message box is currently displayed
3165 * @return {Boolean} True if the message box is visible, else false
3167 isVisible : function(){
3168 return dlg && dlg.isVisible();
3172 * Hides the message box if it is displayed
3175 if(this.isVisible()){
3181 * Displays a new message box, or reinitializes an existing message box, based on the config options
3182 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3183 * The following config object properties are supported:
3185 Property Type Description
3186 ---------- --------------- ------------------------------------------------------------------------------------
3187 animEl String/Element An id or Element from which the message box should animate as it opens and
3188 closes (defaults to undefined)
3189 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3190 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3191 closable Boolean False to hide the top-right close button (defaults to true). Note that
3192 progress and wait dialogs will ignore this property and always hide the
3193 close button as they can only be closed programmatically.
3194 cls String A custom CSS class to apply to the message box element
3195 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3196 displayed (defaults to 75)
3197 fn Function A callback function to execute after closing the dialog. The arguments to the
3198 function will be btn (the name of the button that was clicked, if applicable,
3199 e.g. "ok"), and text (the value of the active text field, if applicable).
3200 Progress and wait dialogs will ignore this option since they do not respond to
3201 user actions and can only be closed programmatically, so any required function
3202 should be called by the same code after it closes the dialog.
3203 icon String A CSS class that provides a background image to be used as an icon for
3204 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3205 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3206 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3207 modal Boolean False to allow user interaction with the page while the message box is
3208 displayed (defaults to true)
3209 msg String A string that will replace the existing message box body text (defaults
3210 to the XHTML-compliant non-breaking space character ' ')
3211 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3212 progress Boolean True to display a progress bar (defaults to false)
3213 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3214 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3215 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3216 title String The title text
3217 value String The string value to set into the active textbox element if displayed
3218 wait Boolean True to display a progress bar (defaults to false)
3219 width Number The width of the dialog in pixels
3226 msg: 'Please enter your address:',
3228 buttons: Roo.MessageBox.OKCANCEL,
3231 animEl: 'addAddressBtn'
3234 * @param {Object} config Configuration options
3235 * @return {Roo.MessageBox} This message box
3237 show : function(options)
3240 // this causes nightmares if you show one dialog after another
3241 // especially on callbacks..
3243 if(this.isVisible()){
3246 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3247 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3248 Roo.log("New Dialog Message:" + options.msg )
3249 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3250 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3253 var d = this.getDialog();
3255 d.setTitle(opt.title || " ");
3256 d.closeEl.setDisplayed(opt.closable !== false);
3257 activeTextEl = textboxEl;
3258 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3263 textareaEl.setHeight(typeof opt.multiline == "number" ?
3264 opt.multiline : this.defaultTextHeight);
3265 activeTextEl = textareaEl;
3274 progressEl.setDisplayed(opt.progress === true);
3275 this.updateProgress(0);
3276 activeTextEl.dom.value = opt.value || "";
3278 dlg.setDefaultButton(activeTextEl);
3280 var bs = opt.buttons;
3284 }else if(bs && bs.yes){
3285 db = buttons["yes"];
3287 dlg.setDefaultButton(db);
3289 bwidth = updateButtons(opt.buttons);
3290 this.updateText(opt.msg);
3292 d.el.addClass(opt.cls);
3294 d.proxyDrag = opt.proxyDrag === true;
3295 d.modal = opt.modal !== false;
3296 d.mask = opt.modal !== false ? mask : false;
3298 // force it to the end of the z-index stack so it gets a cursor in FF
3299 document.body.appendChild(dlg.el.dom);
3300 d.animateTarget = null;
3301 d.show(options.animEl);
3307 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3308 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3309 * and closing the message box when the process is complete.
3310 * @param {String} title The title bar text
3311 * @param {String} msg The message box body text
3312 * @return {Roo.MessageBox} This message box
3314 progress : function(title, msg){
3321 minWidth: this.minProgressWidth,
3328 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3329 * If a callback function is passed it will be called after the user clicks the button, and the
3330 * id of the button that was clicked will be passed as the only parameter to the callback
3331 * (could also be the top-right close button).
3332 * @param {String} title The title bar text
3333 * @param {String} msg The message box body text
3334 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3335 * @param {Object} scope (optional) The scope of the callback function
3336 * @return {Roo.MessageBox} This message box
3338 alert : function(title, msg, fn, scope)
3353 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3354 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3355 * You are responsible for closing the message box when the process is complete.
3356 * @param {String} msg The message box body text
3357 * @param {String} title (optional) The title bar text
3358 * @return {Roo.MessageBox} This message box
3360 wait : function(msg, title){
3371 waitTimer = Roo.TaskMgr.start({
3373 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3381 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3382 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3383 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3384 * @param {String} title The title bar text
3385 * @param {String} msg The message box body text
3386 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3387 * @param {Object} scope (optional) The scope of the callback function
3388 * @return {Roo.MessageBox} This message box
3390 confirm : function(title, msg, fn, scope){
3394 buttons: this.YESNO,
3403 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3404 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3405 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3406 * (could also be the top-right close button) and the text that was entered will be passed as the two
3407 * parameters to the callback.
3408 * @param {String} title The title bar text
3409 * @param {String} msg The message box body text
3410 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3411 * @param {Object} scope (optional) The scope of the callback function
3412 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3413 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3414 * @return {Roo.MessageBox} This message box
3416 prompt : function(title, msg, fn, scope, multiline){
3420 buttons: this.OKCANCEL,
3425 multiline: multiline,
3432 * Button config that displays a single OK button
3437 * Button config that displays Yes and No buttons
3440 YESNO : {yes:true, no:true},
3442 * Button config that displays OK and Cancel buttons
3445 OKCANCEL : {ok:true, cancel:true},
3447 * Button config that displays Yes, No and Cancel buttons
3450 YESNOCANCEL : {yes:true, no:true, cancel:true},
3453 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3456 defaultTextHeight : 75,
3458 * The maximum width in pixels of the message box (defaults to 600)
3463 * The minimum width in pixels of the message box (defaults to 100)
3468 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3469 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3472 minProgressWidth : 250,
3474 * An object containing the default button text strings that can be overriden for localized language support.
3475 * Supported properties are: ok, cancel, yes and no.
3476 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3489 * Shorthand for {@link Roo.MessageBox}
3491 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3492 Roo.Msg = Roo.Msg || Roo.MessageBox;
3501 * @class Roo.bootstrap.Navbar
3502 * @extends Roo.bootstrap.Component
3503 * Bootstrap Navbar class
3506 * Create a new Navbar
3507 * @param {Object} config The config object
3511 Roo.bootstrap.Navbar = function(config){
3512 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3516 * @event beforetoggle
3517 * Fire before toggle the menu
3518 * @param {Roo.EventObject} e
3520 "beforetoggle" : true
3524 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3533 getAutoCreate : function(){
3536 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3540 initEvents :function ()
3542 //Roo.log(this.el.select('.navbar-toggle',true));
3543 this.el.select('.navbar-toggle',true).on('click', function() {
3544 if(this.fireEvent('beforetoggle', this) !== false){
3545 this.el.select('.navbar-collapse',true).toggleClass('in');
3555 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3557 var size = this.el.getSize();
3558 this.maskEl.setSize(size.width, size.height);
3559 this.maskEl.enableDisplayMode("block");
3568 getChildContainer : function()
3570 if (this.el.select('.collapse').getCount()) {
3571 return this.el.select('.collapse',true).first();
3604 * @class Roo.bootstrap.NavSimplebar
3605 * @extends Roo.bootstrap.Navbar
3606 * Bootstrap Sidebar class
3608 * @cfg {Boolean} inverse is inverted color
3610 * @cfg {String} type (nav | pills | tabs)
3611 * @cfg {Boolean} arrangement stacked | justified
3612 * @cfg {String} align (left | right) alignment
3614 * @cfg {Boolean} main (true|false) main nav bar? default false
3615 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3617 * @cfg {String} tag (header|footer|nav|div) default is nav
3623 * Create a new Sidebar
3624 * @param {Object} config The config object
3628 Roo.bootstrap.NavSimplebar = function(config){
3629 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3632 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3648 getAutoCreate : function(){
3652 tag : this.tag || 'div',
3665 this.type = this.type || 'nav';
3666 if (['tabs','pills'].indexOf(this.type)!==-1) {
3667 cfg.cn[0].cls += ' nav-' + this.type
3671 if (this.type!=='nav') {
3672 Roo.log('nav type must be nav/tabs/pills')
3674 cfg.cn[0].cls += ' navbar-nav'
3680 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3681 cfg.cn[0].cls += ' nav-' + this.arrangement;
3685 if (this.align === 'right') {
3686 cfg.cn[0].cls += ' navbar-right';
3690 cfg.cls += ' navbar-inverse';
3717 * @class Roo.bootstrap.NavHeaderbar
3718 * @extends Roo.bootstrap.NavSimplebar
3719 * Bootstrap Sidebar class
3721 * @cfg {String} brand what is brand
3722 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3723 * @cfg {String} brand_href href of the brand
3724 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3725 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3726 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3727 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3730 * Create a new Sidebar
3731 * @param {Object} config The config object
3735 Roo.bootstrap.NavHeaderbar = function(config){
3736 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3740 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3747 desktopCenter : false,
3750 getAutoCreate : function(){
3753 tag: this.nav || 'nav',
3760 if (this.desktopCenter) {
3761 cn.push({cls : 'container', cn : []});
3768 cls: 'navbar-header',
3773 cls: 'navbar-toggle',
3774 'data-toggle': 'collapse',
3779 html: 'Toggle navigation'
3801 cls: 'collapse navbar-collapse',
3805 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3807 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3808 cfg.cls += ' navbar-' + this.position;
3810 // tag can override this..
3812 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3815 if (this.brand !== '') {
3818 href: this.brand_href ? this.brand_href : '#',
3819 cls: 'navbar-brand',
3827 cfg.cls += ' main-nav';
3835 getHeaderChildContainer : function()
3837 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3838 return this.el.select('.navbar-header',true).first();
3841 return this.getChildContainer();
3845 initEvents : function()
3847 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3849 if (this.autohide) {
3854 Roo.get(document).on('scroll',function(e) {
3855 var ns = Roo.get(document).getScroll().top;
3856 var os = prevScroll;
3860 ft.removeClass('slideDown');
3861 ft.addClass('slideUp');
3864 ft.removeClass('slideUp');
3865 ft.addClass('slideDown');
3886 * @class Roo.bootstrap.NavSidebar
3887 * @extends Roo.bootstrap.Navbar
3888 * Bootstrap Sidebar class
3891 * Create a new Sidebar
3892 * @param {Object} config The config object
3896 Roo.bootstrap.NavSidebar = function(config){
3897 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3900 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3902 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3904 getAutoCreate : function(){
3909 cls: 'sidebar sidebar-nav'
3931 * @class Roo.bootstrap.NavGroup
3932 * @extends Roo.bootstrap.Component
3933 * Bootstrap NavGroup class
3934 * @cfg {String} align (left|right)
3935 * @cfg {Boolean} inverse
3936 * @cfg {String} type (nav|pills|tab) default nav
3937 * @cfg {String} navId - reference Id for navbar.
3941 * Create a new nav group
3942 * @param {Object} config The config object
3945 Roo.bootstrap.NavGroup = function(config){
3946 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3949 Roo.bootstrap.NavGroup.register(this);
3953 * Fires when the active item changes
3954 * @param {Roo.bootstrap.NavGroup} this
3955 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3956 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3963 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3974 getAutoCreate : function()
3976 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3983 if (['tabs','pills'].indexOf(this.type)!==-1) {
3984 cfg.cls += ' nav-' + this.type
3986 if (this.type!=='nav') {
3987 Roo.log('nav type must be nav/tabs/pills')
3989 cfg.cls += ' navbar-nav'
3992 if (this.parent().sidebar) {
3995 cls: 'dashboard-menu sidebar-menu'
4001 if (this.form === true) {
4007 if (this.align === 'right') {
4008 cfg.cls += ' navbar-right';
4010 cfg.cls += ' navbar-left';
4014 if (this.align === 'right') {
4015 cfg.cls += ' navbar-right';
4019 cfg.cls += ' navbar-inverse';
4027 * sets the active Navigation item
4028 * @param {Roo.bootstrap.NavItem} the new current navitem
4030 setActiveItem : function(item)
4033 Roo.each(this.navItems, function(v){
4038 v.setActive(false, true);
4045 item.setActive(true, true);
4046 this.fireEvent('changed', this, item, prev);
4051 * gets the active Navigation item
4052 * @return {Roo.bootstrap.NavItem} the current navitem
4054 getActive : function()
4058 Roo.each(this.navItems, function(v){
4069 indexOfNav : function()
4073 Roo.each(this.navItems, function(v,i){
4084 * adds a Navigation item
4085 * @param {Roo.bootstrap.NavItem} the navitem to add
4087 addItem : function(cfg)
4089 var cn = new Roo.bootstrap.NavItem(cfg);
4091 cn.parentId = this.id;
4092 cn.onRender(this.el, null);
4096 * register a Navigation item
4097 * @param {Roo.bootstrap.NavItem} the navitem to add
4099 register : function(item)
4101 this.navItems.push( item);
4102 item.navId = this.navId;
4107 * clear all the Navigation item
4110 clearAll : function()
4113 this.el.dom.innerHTML = '';
4116 getNavItem: function(tabId)
4119 Roo.each(this.navItems, function(e) {
4120 if (e.tabId == tabId) {
4130 setActiveNext : function()
4132 var i = this.indexOfNav(this.getActive());
4133 if (i > this.navItems.length) {
4136 this.setActiveItem(this.navItems[i+1]);
4138 setActivePrev : function()
4140 var i = this.indexOfNav(this.getActive());
4144 this.setActiveItem(this.navItems[i-1]);
4146 clearWasActive : function(except) {
4147 Roo.each(this.navItems, function(e) {
4148 if (e.tabId != except.tabId && e.was_active) {
4149 e.was_active = false;
4156 getWasActive : function ()
4159 Roo.each(this.navItems, function(e) {
4174 Roo.apply(Roo.bootstrap.NavGroup, {
4178 * register a Navigation Group
4179 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4181 register : function(navgrp)
4183 this.groups[navgrp.navId] = navgrp;
4187 * fetch a Navigation Group based on the navigation ID
4188 * @param {string} the navgroup to add
4189 * @returns {Roo.bootstrap.NavGroup} the navgroup
4191 get: function(navId) {
4192 if (typeof(this.groups[navId]) == 'undefined') {
4194 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4196 return this.groups[navId] ;
4211 * @class Roo.bootstrap.NavItem
4212 * @extends Roo.bootstrap.Component
4213 * Bootstrap Navbar.NavItem class
4214 * @cfg {String} href link to
4215 * @cfg {String} html content of button
4216 * @cfg {String} badge text inside badge
4217 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4218 * @cfg {String} glyphicon name of glyphicon
4219 * @cfg {String} icon name of font awesome icon
4220 * @cfg {Boolean} active Is item active
4221 * @cfg {Boolean} disabled Is item disabled
4223 * @cfg {Boolean} preventDefault (true | false) default false
4224 * @cfg {String} tabId the tab that this item activates.
4225 * @cfg {String} tagtype (a|span) render as a href or span?
4226 * @cfg {Boolean} animateRef (true|false) link to element default false
4229 * Create a new Navbar Item
4230 * @param {Object} config The config object
4232 Roo.bootstrap.NavItem = function(config){
4233 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4238 * The raw click event for the entire grid.
4239 * @param {Roo.EventObject} e
4244 * Fires when the active item active state changes
4245 * @param {Roo.bootstrap.NavItem} this
4246 * @param {boolean} state the new state
4252 * Fires when scroll to element
4253 * @param {Roo.bootstrap.NavItem} this
4254 * @param {Object} options
4255 * @param {Roo.EventObject} e
4263 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4271 preventDefault : false,
4278 getAutoCreate : function(){
4287 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4289 if (this.disabled) {
4290 cfg.cls += ' disabled';
4293 if (this.href || this.html || this.glyphicon || this.icon) {
4297 href : this.href || "#",
4298 html: this.html || ''
4303 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4306 if(this.glyphicon) {
4307 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4312 cfg.cn[0].html += " <span class='caret'></span>";
4316 if (this.badge !== '') {
4318 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4326 initEvents: function()
4328 if (typeof (this.menu) != 'undefined') {
4329 this.menu.parentType = this.xtype;
4330 this.menu.triggerEl = this.el;
4331 this.menu = this.addxtype(Roo.apply({}, this.menu));
4334 this.el.select('a',true).on('click', this.onClick, this);
4336 if(this.tagtype == 'span'){
4337 this.el.select('span',true).on('click', this.onClick, this);
4340 // at this point parent should be available..
4341 this.parent().register(this);
4344 onClick : function(e)
4346 if (e.getTarget('.dropdown-menu-item')) {
4347 // did you click on a menu itemm.... - then don't trigger onclick..
4352 this.preventDefault ||
4355 Roo.log("NavItem - prevent Default?");
4359 if (this.disabled) {
4363 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4364 if (tg && tg.transition) {
4365 Roo.log("waiting for the transitionend");
4371 //Roo.log("fire event clicked");
4372 if(this.fireEvent('click', this, e) === false){
4376 if(this.tagtype == 'span'){
4380 //Roo.log(this.href);
4381 var ael = this.el.select('a',true).first();
4384 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4385 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4386 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4387 return; // ignore... - it's a 'hash' to another page.
4389 Roo.log("NavItem - prevent Default?");
4391 this.scrollToElement(e);
4395 var p = this.parent();
4397 if (['tabs','pills'].indexOf(p.type)!==-1) {
4398 if (typeof(p.setActiveItem) !== 'undefined') {
4399 p.setActiveItem(this);
4403 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4404 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4405 // remove the collapsed menu expand...
4406 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4410 isActive: function () {
4413 setActive : function(state, fire, is_was_active)
4415 if (this.active && !state && this.navId) {
4416 this.was_active = true;
4417 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4419 nv.clearWasActive(this);
4423 this.active = state;
4426 this.el.removeClass('active');
4427 } else if (!this.el.hasClass('active')) {
4428 this.el.addClass('active');
4431 this.fireEvent('changed', this, state);
4434 // show a panel if it's registered and related..
4436 if (!this.navId || !this.tabId || !state || is_was_active) {
4440 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4444 var pan = tg.getPanelByName(this.tabId);
4448 // if we can not flip to new panel - go back to old nav highlight..
4449 if (false == tg.showPanel(pan)) {
4450 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4452 var onav = nv.getWasActive();
4454 onav.setActive(true, false, true);
4463 // this should not be here...
4464 setDisabled : function(state)
4466 this.disabled = state;
4468 this.el.removeClass('disabled');
4469 } else if (!this.el.hasClass('disabled')) {
4470 this.el.addClass('disabled');
4476 * Fetch the element to display the tooltip on.
4477 * @return {Roo.Element} defaults to this.el
4479 tooltipEl : function()
4481 return this.el.select('' + this.tagtype + '', true).first();
4484 scrollToElement : function(e)
4486 var c = document.body;
4489 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4491 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4492 c = document.documentElement;
4495 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4501 var o = target.calcOffsetsTo(c);
4508 this.fireEvent('scrollto', this, options, e);
4510 Roo.get(c).scrollTo('top', options.value, true);
4523 * <span> icon </span>
4524 * <span> text </span>
4525 * <span>badge </span>
4529 * @class Roo.bootstrap.NavSidebarItem
4530 * @extends Roo.bootstrap.NavItem
4531 * Bootstrap Navbar.NavSidebarItem class
4532 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4533 * {bool} open is the menu open
4535 * Create a new Navbar Button
4536 * @param {Object} config The config object
4538 Roo.bootstrap.NavSidebarItem = function(config){
4539 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4544 * The raw click event for the entire grid.
4545 * @param {Roo.EventObject} e
4550 * Fires when the active item active state changes
4551 * @param {Roo.bootstrap.NavSidebarItem} this
4552 * @param {boolean} state the new state
4560 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4562 badgeWeight : 'default',
4566 getAutoCreate : function(){
4571 href : this.href || '#',
4583 html : this.html || ''
4588 cfg.cls += ' active';
4591 if (this.disabled) {
4592 cfg.cls += ' disabled';
4595 cfg.cls += ' open x-open';
4598 if (this.glyphicon || this.icon) {
4599 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4600 a.cn.push({ tag : 'i', cls : c }) ;
4605 if (this.badge !== '') {
4607 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4611 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4612 a.cls += 'dropdown-toggle treeview' ;
4620 initEvents : function()
4622 if (typeof (this.menu) != 'undefined') {
4623 this.menu.parentType = this.xtype;
4624 this.menu.triggerEl = this.el;
4625 this.menu = this.addxtype(Roo.apply({}, this.menu));
4628 this.el.on('click', this.onClick, this);
4631 if(this.badge !== ''){
4633 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4638 onClick : function(e)
4645 if(this.preventDefault){
4649 this.fireEvent('click', this);
4652 disable : function()
4654 this.setDisabled(true);
4659 this.setDisabled(false);
4662 setDisabled : function(state)
4664 if(this.disabled == state){
4668 this.disabled = state;
4671 this.el.addClass('disabled');
4675 this.el.removeClass('disabled');
4680 setActive : function(state)
4682 if(this.active == state){
4686 this.active = state;
4689 this.el.addClass('active');
4693 this.el.removeClass('active');
4698 isActive: function ()
4703 setBadge : function(str)
4709 this.badgeEl.dom.innerHTML = str;
4726 * @class Roo.bootstrap.Row
4727 * @extends Roo.bootstrap.Component
4728 * Bootstrap Row class (contains columns...)
4732 * @param {Object} config The config object
4735 Roo.bootstrap.Row = function(config){
4736 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4739 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4741 getAutoCreate : function(){
4760 * @class Roo.bootstrap.Element
4761 * @extends Roo.bootstrap.Component
4762 * Bootstrap Element class
4763 * @cfg {String} html contents of the element
4764 * @cfg {String} tag tag of the element
4765 * @cfg {String} cls class of the element
4766 * @cfg {Boolean} preventDefault (true|false) default false
4767 * @cfg {Boolean} clickable (true|false) default false
4770 * Create a new Element
4771 * @param {Object} config The config object
4774 Roo.bootstrap.Element = function(config){
4775 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4781 * When a element is chick
4782 * @param {Roo.bootstrap.Element} this
4783 * @param {Roo.EventObject} e
4789 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4794 preventDefault: false,
4797 getAutoCreate : function(){
4808 initEvents: function()
4810 Roo.bootstrap.Element.superclass.initEvents.call(this);
4813 this.el.on('click', this.onClick, this);
4818 onClick : function(e)
4820 if(this.preventDefault){
4824 this.fireEvent('click', this, e);
4827 getValue : function()
4829 return this.el.dom.innerHTML;
4832 setValue : function(value)
4834 this.el.dom.innerHTML = value;
4849 * @class Roo.bootstrap.Pagination
4850 * @extends Roo.bootstrap.Component
4851 * Bootstrap Pagination class
4852 * @cfg {String} size xs | sm | md | lg
4853 * @cfg {Boolean} inverse false | true
4856 * Create a new Pagination
4857 * @param {Object} config The config object
4860 Roo.bootstrap.Pagination = function(config){
4861 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4864 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4870 getAutoCreate : function(){
4876 cfg.cls += ' inverse';
4882 cfg.cls += " " + this.cls;
4900 * @class Roo.bootstrap.PaginationItem
4901 * @extends Roo.bootstrap.Component
4902 * Bootstrap PaginationItem class
4903 * @cfg {String} html text
4904 * @cfg {String} href the link
4905 * @cfg {Boolean} preventDefault (true | false) default true
4906 * @cfg {Boolean} active (true | false) default false
4907 * @cfg {Boolean} disabled default false
4911 * Create a new PaginationItem
4912 * @param {Object} config The config object
4916 Roo.bootstrap.PaginationItem = function(config){
4917 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4922 * The raw click event for the entire grid.
4923 * @param {Roo.EventObject} e
4929 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4933 preventDefault: true,
4938 getAutoCreate : function(){
4944 href : this.href ? this.href : '#',
4945 html : this.html ? this.html : ''
4955 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4959 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4965 initEvents: function() {
4967 this.el.on('click', this.onClick, this);
4970 onClick : function(e)
4972 Roo.log('PaginationItem on click ');
4973 if(this.preventDefault){
4981 this.fireEvent('click', this, e);
4997 * @class Roo.bootstrap.Slider
4998 * @extends Roo.bootstrap.Component
4999 * Bootstrap Slider class
5002 * Create a new Slider
5003 * @param {Object} config The config object
5006 Roo.bootstrap.Slider = function(config){
5007 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5010 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5012 getAutoCreate : function(){
5016 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5020 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5032 * Ext JS Library 1.1.1
5033 * Copyright(c) 2006-2007, Ext JS, LLC.
5035 * Originally Released Under LGPL - original licence link has changed is not relivant.
5038 * <script type="text/javascript">
5043 * @class Roo.grid.ColumnModel
5044 * @extends Roo.util.Observable
5045 * This is the default implementation of a ColumnModel used by the Grid. It defines
5046 * the columns in the grid.
5049 var colModel = new Roo.grid.ColumnModel([
5050 {header: "Ticker", width: 60, sortable: true, locked: true},
5051 {header: "Company Name", width: 150, sortable: true},
5052 {header: "Market Cap.", width: 100, sortable: true},
5053 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5054 {header: "Employees", width: 100, sortable: true, resizable: false}
5059 * The config options listed for this class are options which may appear in each
5060 * individual column definition.
5061 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5063 * @param {Object} config An Array of column config objects. See this class's
5064 * config objects for details.
5066 Roo.grid.ColumnModel = function(config){
5068 * The config passed into the constructor
5070 this.config = config;
5073 // if no id, create one
5074 // if the column does not have a dataIndex mapping,
5075 // map it to the order it is in the config
5076 for(var i = 0, len = config.length; i < len; i++){
5078 if(typeof c.dataIndex == "undefined"){
5081 if(typeof c.renderer == "string"){
5082 c.renderer = Roo.util.Format[c.renderer];
5084 if(typeof c.id == "undefined"){
5087 if(c.editor && c.editor.xtype){
5088 c.editor = Roo.factory(c.editor, Roo.grid);
5090 if(c.editor && c.editor.isFormField){
5091 c.editor = new Roo.grid.GridEditor(c.editor);
5093 this.lookup[c.id] = c;
5097 * The width of columns which have no width specified (defaults to 100)
5100 this.defaultWidth = 100;
5103 * Default sortable of columns which have no sortable specified (defaults to false)
5106 this.defaultSortable = false;
5110 * @event widthchange
5111 * Fires when the width of a column changes.
5112 * @param {ColumnModel} this
5113 * @param {Number} columnIndex The column index
5114 * @param {Number} newWidth The new width
5116 "widthchange": true,
5118 * @event headerchange
5119 * Fires when the text of a header changes.
5120 * @param {ColumnModel} this
5121 * @param {Number} columnIndex The column index
5122 * @param {Number} newText The new header text
5124 "headerchange": true,
5126 * @event hiddenchange
5127 * Fires when a column is hidden or "unhidden".
5128 * @param {ColumnModel} this
5129 * @param {Number} columnIndex The column index
5130 * @param {Boolean} hidden true if hidden, false otherwise
5132 "hiddenchange": true,
5134 * @event columnmoved
5135 * Fires when a column is moved.
5136 * @param {ColumnModel} this
5137 * @param {Number} oldIndex
5138 * @param {Number} newIndex
5140 "columnmoved" : true,
5142 * @event columlockchange
5143 * Fires when a column's locked state is changed
5144 * @param {ColumnModel} this
5145 * @param {Number} colIndex
5146 * @param {Boolean} locked true if locked
5148 "columnlockchange" : true
5150 Roo.grid.ColumnModel.superclass.constructor.call(this);
5152 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5154 * @cfg {String} header The header text to display in the Grid view.
5157 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5158 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5159 * specified, the column's index is used as an index into the Record's data Array.
5162 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5163 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5166 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5167 * Defaults to the value of the {@link #defaultSortable} property.
5168 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5171 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5174 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5177 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5180 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5183 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5184 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5185 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5186 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5189 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5192 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5195 * @cfg {String} cursor (Optional)
5198 * @cfg {String} tooltip (Optional)
5201 * @cfg {Number} xs (Optional)
5204 * @cfg {Number} sm (Optional)
5207 * @cfg {Number} md (Optional)
5210 * @cfg {Number} lg (Optional)
5213 * Returns the id of the column at the specified index.
5214 * @param {Number} index The column index
5215 * @return {String} the id
5217 getColumnId : function(index){
5218 return this.config[index].id;
5222 * Returns the column for a specified id.
5223 * @param {String} id The column id
5224 * @return {Object} the column
5226 getColumnById : function(id){
5227 return this.lookup[id];
5232 * Returns the column for a specified dataIndex.
5233 * @param {String} dataIndex The column dataIndex
5234 * @return {Object|Boolean} the column or false if not found
5236 getColumnByDataIndex: function(dataIndex){
5237 var index = this.findColumnIndex(dataIndex);
5238 return index > -1 ? this.config[index] : false;
5242 * Returns the index for a specified column id.
5243 * @param {String} id The column id
5244 * @return {Number} the index, or -1 if not found
5246 getIndexById : function(id){
5247 for(var i = 0, len = this.config.length; i < len; i++){
5248 if(this.config[i].id == id){
5256 * Returns the index for a specified column dataIndex.
5257 * @param {String} dataIndex The column dataIndex
5258 * @return {Number} the index, or -1 if not found
5261 findColumnIndex : function(dataIndex){
5262 for(var i = 0, len = this.config.length; i < len; i++){
5263 if(this.config[i].dataIndex == dataIndex){
5271 moveColumn : function(oldIndex, newIndex){
5272 var c = this.config[oldIndex];
5273 this.config.splice(oldIndex, 1);
5274 this.config.splice(newIndex, 0, c);
5275 this.dataMap = null;
5276 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5279 isLocked : function(colIndex){
5280 return this.config[colIndex].locked === true;
5283 setLocked : function(colIndex, value, suppressEvent){
5284 if(this.isLocked(colIndex) == value){
5287 this.config[colIndex].locked = value;
5289 this.fireEvent("columnlockchange", this, colIndex, value);
5293 getTotalLockedWidth : function(){
5295 for(var i = 0; i < this.config.length; i++){
5296 if(this.isLocked(i) && !this.isHidden(i)){
5297 this.totalWidth += this.getColumnWidth(i);
5303 getLockedCount : function(){
5304 for(var i = 0, len = this.config.length; i < len; i++){
5305 if(!this.isLocked(i)){
5310 return this.config.length;
5314 * Returns the number of columns.
5317 getColumnCount : function(visibleOnly){
5318 if(visibleOnly === true){
5320 for(var i = 0, len = this.config.length; i < len; i++){
5321 if(!this.isHidden(i)){
5327 return this.config.length;
5331 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5332 * @param {Function} fn
5333 * @param {Object} scope (optional)
5334 * @return {Array} result
5336 getColumnsBy : function(fn, scope){
5338 for(var i = 0, len = this.config.length; i < len; i++){
5339 var c = this.config[i];
5340 if(fn.call(scope||this, c, i) === true){
5348 * Returns true if the specified column is sortable.
5349 * @param {Number} col The column index
5352 isSortable : function(col){
5353 if(typeof this.config[col].sortable == "undefined"){
5354 return this.defaultSortable;
5356 return this.config[col].sortable;
5360 * Returns the rendering (formatting) function defined for the column.
5361 * @param {Number} col The column index.
5362 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5364 getRenderer : function(col){
5365 if(!this.config[col].renderer){
5366 return Roo.grid.ColumnModel.defaultRenderer;
5368 return this.config[col].renderer;
5372 * Sets the rendering (formatting) function for a column.
5373 * @param {Number} col The column index
5374 * @param {Function} fn The function to use to process the cell's raw data
5375 * to return HTML markup for the grid view. The render function is called with
5376 * the following parameters:<ul>
5377 * <li>Data value.</li>
5378 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5379 * <li>css A CSS style string to apply to the table cell.</li>
5380 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5381 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5382 * <li>Row index</li>
5383 * <li>Column index</li>
5384 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5386 setRenderer : function(col, fn){
5387 this.config[col].renderer = fn;
5391 * Returns the width for the specified column.
5392 * @param {Number} col The column index
5395 getColumnWidth : function(col){
5396 return this.config[col].width * 1 || this.defaultWidth;
5400 * Sets the width for a column.
5401 * @param {Number} col The column index
5402 * @param {Number} width The new width
5404 setColumnWidth : function(col, width, suppressEvent){
5405 this.config[col].width = width;
5406 this.totalWidth = null;
5408 this.fireEvent("widthchange", this, col, width);
5413 * Returns the total width of all columns.
5414 * @param {Boolean} includeHidden True to include hidden column widths
5417 getTotalWidth : function(includeHidden){
5418 if(!this.totalWidth){
5419 this.totalWidth = 0;
5420 for(var i = 0, len = this.config.length; i < len; i++){
5421 if(includeHidden || !this.isHidden(i)){
5422 this.totalWidth += this.getColumnWidth(i);
5426 return this.totalWidth;
5430 * Returns the header for the specified column.
5431 * @param {Number} col The column index
5434 getColumnHeader : function(col){
5435 return this.config[col].header;
5439 * Sets the header for a column.
5440 * @param {Number} col The column index
5441 * @param {String} header The new header
5443 setColumnHeader : function(col, header){
5444 this.config[col].header = header;
5445 this.fireEvent("headerchange", this, col, header);
5449 * Returns the tooltip for the specified column.
5450 * @param {Number} col The column index
5453 getColumnTooltip : function(col){
5454 return this.config[col].tooltip;
5457 * Sets the tooltip for a column.
5458 * @param {Number} col The column index
5459 * @param {String} tooltip The new tooltip
5461 setColumnTooltip : function(col, tooltip){
5462 this.config[col].tooltip = tooltip;
5466 * Returns the dataIndex for the specified column.
5467 * @param {Number} col The column index
5470 getDataIndex : function(col){
5471 return this.config[col].dataIndex;
5475 * Sets the dataIndex for a column.
5476 * @param {Number} col The column index
5477 * @param {Number} dataIndex The new dataIndex
5479 setDataIndex : function(col, dataIndex){
5480 this.config[col].dataIndex = dataIndex;
5486 * Returns true if the cell is editable.
5487 * @param {Number} colIndex The column index
5488 * @param {Number} rowIndex The row index - this is nto actually used..?
5491 isCellEditable : function(colIndex, rowIndex){
5492 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5496 * Returns the editor defined for the cell/column.
5497 * return false or null to disable editing.
5498 * @param {Number} colIndex The column index
5499 * @param {Number} rowIndex The row index
5502 getCellEditor : function(colIndex, rowIndex){
5503 return this.config[colIndex].editor;
5507 * Sets if a column is editable.
5508 * @param {Number} col The column index
5509 * @param {Boolean} editable True if the column is editable
5511 setEditable : function(col, editable){
5512 this.config[col].editable = editable;
5517 * Returns true if the column is hidden.
5518 * @param {Number} colIndex The column index
5521 isHidden : function(colIndex){
5522 return this.config[colIndex].hidden;
5527 * Returns true if the column width cannot be changed
5529 isFixed : function(colIndex){
5530 return this.config[colIndex].fixed;
5534 * Returns true if the column can be resized
5537 isResizable : function(colIndex){
5538 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5541 * Sets if a column is hidden.
5542 * @param {Number} colIndex The column index
5543 * @param {Boolean} hidden True if the column is hidden
5545 setHidden : function(colIndex, hidden){
5546 this.config[colIndex].hidden = hidden;
5547 this.totalWidth = null;
5548 this.fireEvent("hiddenchange", this, colIndex, hidden);
5552 * Sets the editor for a column.
5553 * @param {Number} col The column index
5554 * @param {Object} editor The editor object
5556 setEditor : function(col, editor){
5557 this.config[col].editor = editor;
5561 Roo.grid.ColumnModel.defaultRenderer = function(value)
5563 if(typeof value == "object") {
5566 if(typeof value == "string" && value.length < 1){
5570 return String.format("{0}", value);
5573 // Alias for backwards compatibility
5574 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5577 * Ext JS Library 1.1.1
5578 * Copyright(c) 2006-2007, Ext JS, LLC.
5580 * Originally Released Under LGPL - original licence link has changed is not relivant.
5583 * <script type="text/javascript">
5587 * @class Roo.LoadMask
5588 * A simple utility class for generically masking elements while loading data. If the element being masked has
5589 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5590 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5591 * element's UpdateManager load indicator and will be destroyed after the initial load.
5593 * Create a new LoadMask
5594 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5595 * @param {Object} config The config object
5597 Roo.LoadMask = function(el, config){
5598 this.el = Roo.get(el);
5599 Roo.apply(this, config);
5601 this.store.on('beforeload', this.onBeforeLoad, this);
5602 this.store.on('load', this.onLoad, this);
5603 this.store.on('loadexception', this.onLoadException, this);
5604 this.removeMask = false;
5606 var um = this.el.getUpdateManager();
5607 um.showLoadIndicator = false; // disable the default indicator
5608 um.on('beforeupdate', this.onBeforeLoad, this);
5609 um.on('update', this.onLoad, this);
5610 um.on('failure', this.onLoad, this);
5611 this.removeMask = true;
5615 Roo.LoadMask.prototype = {
5617 * @cfg {Boolean} removeMask
5618 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5619 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5623 * The text to display in a centered loading message box (defaults to 'Loading...')
5627 * @cfg {String} msgCls
5628 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5630 msgCls : 'x-mask-loading',
5633 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5639 * Disables the mask to prevent it from being displayed
5641 disable : function(){
5642 this.disabled = true;
5646 * Enables the mask so that it can be displayed
5648 enable : function(){
5649 this.disabled = false;
5652 onLoadException : function()
5656 if (typeof(arguments[3]) != 'undefined') {
5657 Roo.MessageBox.alert("Error loading",arguments[3]);
5661 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5662 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5671 this.el.unmask(this.removeMask);
5676 this.el.unmask(this.removeMask);
5680 onBeforeLoad : function(){
5682 this.el.mask(this.msg, this.msgCls);
5687 destroy : function(){
5689 this.store.un('beforeload', this.onBeforeLoad, this);
5690 this.store.un('load', this.onLoad, this);
5691 this.store.un('loadexception', this.onLoadException, this);
5693 var um = this.el.getUpdateManager();
5694 um.un('beforeupdate', this.onBeforeLoad, this);
5695 um.un('update', this.onLoad, this);
5696 um.un('failure', this.onLoad, this);
5707 * @class Roo.bootstrap.Table
5708 * @extends Roo.bootstrap.Component
5709 * Bootstrap Table class
5710 * @cfg {String} cls table class
5711 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5712 * @cfg {String} bgcolor Specifies the background color for a table
5713 * @cfg {Number} border Specifies whether the table cells should have borders or not
5714 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5715 * @cfg {Number} cellspacing Specifies the space between cells
5716 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5717 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5718 * @cfg {String} sortable Specifies that the table should be sortable
5719 * @cfg {String} summary Specifies a summary of the content of a table
5720 * @cfg {Number} width Specifies the width of a table
5721 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5723 * @cfg {boolean} striped Should the rows be alternative striped
5724 * @cfg {boolean} bordered Add borders to the table
5725 * @cfg {boolean} hover Add hover highlighting
5726 * @cfg {boolean} condensed Format condensed
5727 * @cfg {boolean} responsive Format condensed
5728 * @cfg {Boolean} loadMask (true|false) default false
5729 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5730 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5731 * @cfg {Boolean} rowSelection (true|false) default false
5732 * @cfg {Boolean} cellSelection (true|false) default false
5733 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5734 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5738 * Create a new Table
5739 * @param {Object} config The config object
5742 Roo.bootstrap.Table = function(config){
5743 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5748 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5749 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5750 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5751 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5753 this.sm = this.sm || {xtype: 'RowSelectionModel'};
5755 this.sm.grid = this;
5756 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5757 this.sm = this.selModel;
5758 this.sm.xmodule = this.xmodule || false;
5761 if (this.cm && typeof(this.cm.config) == 'undefined') {
5762 this.colModel = new Roo.grid.ColumnModel(this.cm);
5763 this.cm = this.colModel;
5764 this.cm.xmodule = this.xmodule || false;
5767 this.store= Roo.factory(this.store, Roo.data);
5768 this.ds = this.store;
5769 this.ds.xmodule = this.xmodule || false;
5772 if (this.footer && this.store) {
5773 this.footer.dataSource = this.ds;
5774 this.footer = Roo.factory(this.footer);
5781 * Fires when a cell is clicked
5782 * @param {Roo.bootstrap.Table} this
5783 * @param {Roo.Element} el
5784 * @param {Number} rowIndex
5785 * @param {Number} columnIndex
5786 * @param {Roo.EventObject} e
5790 * @event celldblclick
5791 * Fires when a cell is double clicked
5792 * @param {Roo.bootstrap.Table} this
5793 * @param {Roo.Element} el
5794 * @param {Number} rowIndex
5795 * @param {Number} columnIndex
5796 * @param {Roo.EventObject} e
5798 "celldblclick" : true,
5801 * Fires when a row is clicked
5802 * @param {Roo.bootstrap.Table} this
5803 * @param {Roo.Element} el
5804 * @param {Number} rowIndex
5805 * @param {Roo.EventObject} e
5809 * @event rowdblclick
5810 * Fires when a row is double clicked
5811 * @param {Roo.bootstrap.Table} this
5812 * @param {Roo.Element} el
5813 * @param {Number} rowIndex
5814 * @param {Roo.EventObject} e
5816 "rowdblclick" : true,
5819 * Fires when a mouseover occur
5820 * @param {Roo.bootstrap.Table} this
5821 * @param {Roo.Element} el
5822 * @param {Number} rowIndex
5823 * @param {Number} columnIndex
5824 * @param {Roo.EventObject} e
5829 * Fires when a mouseout occur
5830 * @param {Roo.bootstrap.Table} this
5831 * @param {Roo.Element} el
5832 * @param {Number} rowIndex
5833 * @param {Number} columnIndex
5834 * @param {Roo.EventObject} e
5839 * Fires when a row is rendered, so you can change add a style to it.
5840 * @param {Roo.bootstrap.Table} this
5841 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5845 * @event rowsrendered
5846 * Fires when all the rows have been rendered
5847 * @param {Roo.bootstrap.Table} this
5849 'rowsrendered' : true,
5851 * @event contextmenu
5852 * The raw contextmenu event for the entire grid.
5853 * @param {Roo.EventObject} e
5855 "contextmenu" : true,
5857 * @event rowcontextmenu
5858 * Fires when a row is right clicked
5859 * @param {Roo.bootstrap.Table} this
5860 * @param {Number} rowIndex
5861 * @param {Roo.EventObject} e
5863 "rowcontextmenu" : true,
5865 * @event cellcontextmenu
5866 * Fires when a cell is right clicked
5867 * @param {Roo.bootstrap.Table} this
5868 * @param {Number} rowIndex
5869 * @param {Number} cellIndex
5870 * @param {Roo.EventObject} e
5872 "cellcontextmenu" : true,
5874 * @event headercontextmenu
5875 * Fires when a header is right clicked
5876 * @param {Roo.bootstrap.Table} this
5877 * @param {Number} columnIndex
5878 * @param {Roo.EventObject} e
5880 "headercontextmenu" : true
5884 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5910 rowSelection : false,
5911 cellSelection : false,
5914 // Roo.Element - the tbody
5916 // Roo.Element - thead element
5919 container: false, // used by gridpanel...
5921 getAutoCreate : function()
5923 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5930 if (this.scrollBody) {
5931 cfg.cls += ' table-body-fixed';
5934 cfg.cls += ' table-striped';
5938 cfg.cls += ' table-hover';
5940 if (this.bordered) {
5941 cfg.cls += ' table-bordered';
5943 if (this.condensed) {
5944 cfg.cls += ' table-condensed';
5946 if (this.responsive) {
5947 cfg.cls += ' table-responsive';
5951 cfg.cls+= ' ' +this.cls;
5954 // this lot should be simplifed...
5957 cfg.align=this.align;
5960 cfg.bgcolor=this.bgcolor;
5963 cfg.border=this.border;
5965 if (this.cellpadding) {
5966 cfg.cellpadding=this.cellpadding;
5968 if (this.cellspacing) {
5969 cfg.cellspacing=this.cellspacing;
5972 cfg.frame=this.frame;
5975 cfg.rules=this.rules;
5977 if (this.sortable) {
5978 cfg.sortable=this.sortable;
5981 cfg.summary=this.summary;
5984 cfg.width=this.width;
5987 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5990 if(this.store || this.cm){
5991 if(this.headerShow){
5992 cfg.cn.push(this.renderHeader());
5995 cfg.cn.push(this.renderBody());
5997 if(this.footerShow){
5998 cfg.cn.push(this.renderFooter());
6000 // where does this come from?
6001 //cfg.cls+= ' TableGrid';
6004 return { cn : [ cfg ] };
6007 initEvents : function()
6009 if(!this.store || !this.cm){
6012 if (this.selModel) {
6013 this.selModel.initEvents();
6017 //Roo.log('initEvents with ds!!!!');
6019 this.mainBody = this.el.select('tbody', true).first();
6020 this.mainHead = this.el.select('thead', true).first();
6027 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6028 e.on('click', _this.sort, _this);
6031 this.mainBody.on("click", this.onClick, this);
6032 this.mainBody.on("dblclick", this.onDblClick, this);
6034 // why is this done????? = it breaks dialogs??
6035 //this.parent().el.setStyle('position', 'relative');
6039 this.footer.parentId = this.id;
6040 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6043 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6045 this.store.on('load', this.onLoad, this);
6046 this.store.on('beforeload', this.onBeforeLoad, this);
6047 this.store.on('update', this.onUpdate, this);
6048 this.store.on('add', this.onAdd, this);
6049 this.store.on("clear", this.clear, this);
6051 this.el.on("contextmenu", this.onContextMenu, this);
6053 this.mainBody.on('scroll', this.onBodyScroll, this);
6058 onContextMenu : function(e, t)
6060 this.processEvent("contextmenu", e);
6063 processEvent : function(name, e)
6065 if (name != 'touchstart' ) {
6066 this.fireEvent(name, e);
6069 var t = e.getTarget();
6071 var cell = Roo.get(t);
6077 if(cell.findParent('tfoot', false, true)){
6081 if(cell.findParent('thead', false, true)){
6083 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6084 cell = Roo.get(t).findParent('th', false, true);
6086 Roo.log("failed to find th in thead?");
6087 Roo.log(e.getTarget());
6092 var cellIndex = cell.dom.cellIndex;
6094 var ename = name == 'touchstart' ? 'click' : name;
6095 this.fireEvent("header" + ename, this, cellIndex, e);
6100 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6101 cell = Roo.get(t).findParent('td', false, true);
6103 Roo.log("failed to find th in tbody?");
6104 Roo.log(e.getTarget());
6109 var row = cell.findParent('tr', false, true);
6110 var cellIndex = cell.dom.cellIndex;
6111 var rowIndex = row.dom.rowIndex - 1;
6115 this.fireEvent("row" + name, this, rowIndex, e);
6119 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6125 onMouseover : function(e, el)
6127 var cell = Roo.get(el);
6133 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6134 cell = cell.findParent('td', false, true);
6137 var row = cell.findParent('tr', false, true);
6138 var cellIndex = cell.dom.cellIndex;
6139 var rowIndex = row.dom.rowIndex - 1; // start from 0
6141 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6145 onMouseout : function(e, el)
6147 var cell = Roo.get(el);
6153 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6154 cell = cell.findParent('td', false, true);
6157 var row = cell.findParent('tr', false, true);
6158 var cellIndex = cell.dom.cellIndex;
6159 var rowIndex = row.dom.rowIndex - 1; // start from 0
6161 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6165 onClick : function(e, el)
6167 var cell = Roo.get(el);
6169 if(!cell || (!this.cellSelection && !this.rowSelection)){
6173 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6174 cell = cell.findParent('td', false, true);
6177 if(!cell || typeof(cell) == 'undefined'){
6181 var row = cell.findParent('tr', false, true);
6183 if(!row || typeof(row) == 'undefined'){
6187 var cellIndex = cell.dom.cellIndex;
6188 var rowIndex = this.getRowIndex(row);
6190 // why??? - should these not be based on SelectionModel?
6191 if(this.cellSelection){
6192 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6195 if(this.rowSelection){
6196 this.fireEvent('rowclick', this, row, rowIndex, e);
6202 onDblClick : function(e,el)
6204 var cell = Roo.get(el);
6206 if(!cell || (!this.cellSelection && !this.rowSelection)){
6210 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6211 cell = cell.findParent('td', false, true);
6214 if(!cell || typeof(cell) == 'undefined'){
6218 var row = cell.findParent('tr', false, true);
6220 if(!row || typeof(row) == 'undefined'){
6224 var cellIndex = cell.dom.cellIndex;
6225 var rowIndex = this.getRowIndex(row);
6227 if(this.cellSelection){
6228 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6231 if(this.rowSelection){
6232 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6236 sort : function(e,el)
6238 var col = Roo.get(el);
6240 if(!col.hasClass('sortable')){
6244 var sort = col.attr('sort');
6247 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6251 this.store.sortInfo = {field : sort, direction : dir};
6254 Roo.log("calling footer first");
6255 this.footer.onClick('first');
6258 this.store.load({ params : { start : 0 } });
6262 renderHeader : function()
6270 this.totalWidth = 0;
6272 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6274 var config = cm.config[i];
6279 html: cm.getColumnHeader(i)
6284 if(typeof(config.sortable) != 'undefined' && config.sortable){
6286 c.html = '<i class="glyphicon"></i>' + c.html;
6289 if(typeof(config.lgHeader) != 'undefined'){
6290 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6293 if(typeof(config.mdHeader) != 'undefined'){
6294 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6297 if(typeof(config.smHeader) != 'undefined'){
6298 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6301 if(typeof(config.xsHeader) != 'undefined'){
6302 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6309 if(typeof(config.tooltip) != 'undefined'){
6310 c.tooltip = config.tooltip;
6313 if(typeof(config.colspan) != 'undefined'){
6314 c.colspan = config.colspan;
6317 if(typeof(config.hidden) != 'undefined' && config.hidden){
6318 c.style += ' display:none;';
6321 if(typeof(config.dataIndex) != 'undefined'){
6322 c.sort = config.dataIndex;
6327 if(typeof(config.align) != 'undefined' && config.align.length){
6328 c.style += ' text-align:' + config.align + ';';
6331 if(typeof(config.width) != 'undefined'){
6332 c.style += ' width:' + config.width + 'px;';
6333 this.totalWidth += config.width;
6335 this.totalWidth += 100; // assume minimum of 100 per column?
6338 if(typeof(config.cls) != 'undefined'){
6339 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6342 ['xs','sm','md','lg'].map(function(size){
6344 if(typeof(config[size]) == 'undefined'){
6348 if (!config[size]) { // 0 = hidden
6349 c.cls += ' hidden-' + size;
6353 c.cls += ' col-' + size + '-' + config[size];
6363 renderBody : function()
6373 colspan : this.cm.getColumnCount()
6383 renderFooter : function()
6393 colspan : this.cm.getColumnCount()
6407 // Roo.log('ds onload');
6412 var ds = this.store;
6414 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6415 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6416 if (_this.store.sortInfo) {
6418 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6419 e.select('i', true).addClass(['glyphicon-arrow-up']);
6422 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6423 e.select('i', true).addClass(['glyphicon-arrow-down']);
6428 var tbody = this.mainBody;
6430 if(ds.getCount() > 0){
6431 ds.data.each(function(d,rowIndex){
6432 var row = this.renderRow(cm, ds, rowIndex);
6434 tbody.createChild(row);
6438 if(row.cellObjects.length){
6439 Roo.each(row.cellObjects, function(r){
6440 _this.renderCellObject(r);
6447 Roo.each(this.el.select('tbody td', true).elements, function(e){
6448 e.on('mouseover', _this.onMouseover, _this);
6451 Roo.each(this.el.select('tbody td', true).elements, function(e){
6452 e.on('mouseout', _this.onMouseout, _this);
6454 this.fireEvent('rowsrendered', this);
6455 //if(this.loadMask){
6456 // this.maskEl.hide();
6463 onUpdate : function(ds,record)
6465 this.refreshRow(record);
6469 onRemove : function(ds, record, index, isUpdate){
6470 if(isUpdate !== true){
6471 this.fireEvent("beforerowremoved", this, index, record);
6473 var bt = this.mainBody.dom;
6475 var rows = this.el.select('tbody > tr', true).elements;
6477 if(typeof(rows[index]) != 'undefined'){
6478 bt.removeChild(rows[index].dom);
6481 // if(bt.rows[index]){
6482 // bt.removeChild(bt.rows[index]);
6485 if(isUpdate !== true){
6486 //this.stripeRows(index);
6487 //this.syncRowHeights(index, index);
6489 this.fireEvent("rowremoved", this, index, record);
6493 onAdd : function(ds, records, rowIndex)
6495 //Roo.log('on Add called');
6496 // - note this does not handle multiple adding very well..
6497 var bt = this.mainBody.dom;
6498 for (var i =0 ; i < records.length;i++) {
6499 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6500 //Roo.log(records[i]);
6501 //Roo.log(this.store.getAt(rowIndex+i));
6502 this.insertRow(this.store, rowIndex + i, false);
6509 refreshRow : function(record){
6510 var ds = this.store, index;
6511 if(typeof record == 'number'){
6513 record = ds.getAt(index);
6515 index = ds.indexOf(record);
6517 this.insertRow(ds, index, true);
6519 this.onRemove(ds, record, index+1, true);
6521 //this.syncRowHeights(index, index);
6523 this.fireEvent("rowupdated", this, index, record);
6526 insertRow : function(dm, rowIndex, isUpdate){
6529 this.fireEvent("beforerowsinserted", this, rowIndex);
6531 //var s = this.getScrollState();
6532 var row = this.renderRow(this.cm, this.store, rowIndex);
6533 // insert before rowIndex..
6534 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6538 if(row.cellObjects.length){
6539 Roo.each(row.cellObjects, function(r){
6540 _this.renderCellObject(r);
6545 this.fireEvent("rowsinserted", this, rowIndex);
6546 //this.syncRowHeights(firstRow, lastRow);
6547 //this.stripeRows(firstRow);
6554 getRowDom : function(rowIndex)
6556 var rows = this.el.select('tbody > tr', true).elements;
6558 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6561 // returns the object tree for a tr..
6564 renderRow : function(cm, ds, rowIndex)
6567 var d = ds.getAt(rowIndex);
6574 var cellObjects = [];
6576 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6577 var config = cm.config[i];
6579 var renderer = cm.getRenderer(i);
6583 if(typeof(renderer) !== 'undefined'){
6584 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6586 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6587 // and are rendered into the cells after the row is rendered - using the id for the element.
6589 if(typeof(value) === 'object'){
6599 rowIndex : rowIndex,
6604 this.fireEvent('rowclass', this, rowcfg);
6608 cls : rowcfg.rowClass,
6610 html: (typeof(value) === 'object') ? '' : value
6617 if(typeof(config.colspan) != 'undefined'){
6618 td.colspan = config.colspan;
6621 if(typeof(config.hidden) != 'undefined' && config.hidden){
6622 td.style += ' display:none;';
6625 if(typeof(config.align) != 'undefined' && config.align.length){
6626 td.style += ' text-align:' + config.align + ';';
6629 if(typeof(config.width) != 'undefined'){
6630 td.style += ' width:' + config.width + 'px;';
6633 if(typeof(config.cursor) != 'undefined'){
6634 td.style += ' cursor:' + config.cursor + ';';
6637 if(typeof(config.cls) != 'undefined'){
6638 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6641 ['xs','sm','md','lg'].map(function(size){
6643 if(typeof(config[size]) == 'undefined'){
6647 if (!config[size]) { // 0 = hidden
6648 td.cls += ' hidden-' + size;
6652 td.cls += ' col-' + size + '-' + config[size];
6660 row.cellObjects = cellObjects;
6668 onBeforeLoad : function()
6670 //Roo.log('ds onBeforeLoad');
6674 //if(this.loadMask){
6675 // this.maskEl.show();
6683 this.el.select('tbody', true).first().dom.innerHTML = '';
6686 * Show or hide a row.
6687 * @param {Number} rowIndex to show or hide
6688 * @param {Boolean} state hide
6690 setRowVisibility : function(rowIndex, state)
6692 var bt = this.mainBody.dom;
6694 var rows = this.el.select('tbody > tr', true).elements;
6696 if(typeof(rows[rowIndex]) == 'undefined'){
6699 rows[rowIndex].dom.style.display = state ? '' : 'none';
6703 getSelectionModel : function(){
6705 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6707 return this.selModel;
6710 * Render the Roo.bootstrap object from renderder
6712 renderCellObject : function(r)
6716 var t = r.cfg.render(r.container);
6719 Roo.each(r.cfg.cn, function(c){
6721 container: t.getChildContainer(),
6724 _this.renderCellObject(child);
6729 getRowIndex : function(row)
6733 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6744 * Returns the grid's underlying element = used by panel.Grid
6745 * @return {Element} The element
6747 getGridEl : function(){
6751 * Forces a resize - used by panel.Grid
6752 * @return {Element} The element
6754 autoSize : function()
6756 //var ctr = Roo.get(this.container.dom.parentElement);
6757 var ctr = Roo.get(this.el.dom);
6759 var thd = this.getGridEl().select('thead',true).first();
6760 var tbd = this.getGridEl().select('tbody', true).first();
6761 var tfd = this.getGridEl().select('tfoot', true).first();
6763 var cw = ctr.getWidth();
6767 tbd.setSize(ctr.getWidth(),
6768 ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6770 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6773 cw = Math.max(cw, this.totalWidth);
6774 this.getGridEl().select('tr',true).setWidth(cw);
6775 // resize 'expandable coloumn?
6777 return; // we doe not have a view in this design..
6780 onBodyScroll: function()
6783 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6784 this.mainHead.setStyle({
6785 'position' : 'relative',
6786 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6803 * @class Roo.bootstrap.TableCell
6804 * @extends Roo.bootstrap.Component
6805 * Bootstrap TableCell class
6806 * @cfg {String} html cell contain text
6807 * @cfg {String} cls cell class
6808 * @cfg {String} tag cell tag (td|th) default td
6809 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6810 * @cfg {String} align Aligns the content in a cell
6811 * @cfg {String} axis Categorizes cells
6812 * @cfg {String} bgcolor Specifies the background color of a cell
6813 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6814 * @cfg {Number} colspan Specifies the number of columns a cell should span
6815 * @cfg {String} headers Specifies one or more header cells a cell is related to
6816 * @cfg {Number} height Sets the height of a cell
6817 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6818 * @cfg {Number} rowspan Sets the number of rows a cell should span
6819 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6820 * @cfg {String} valign Vertical aligns the content in a cell
6821 * @cfg {Number} width Specifies the width of a cell
6824 * Create a new TableCell
6825 * @param {Object} config The config object
6828 Roo.bootstrap.TableCell = function(config){
6829 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6832 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6852 getAutoCreate : function(){
6853 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6873 cfg.align=this.align
6879 cfg.bgcolor=this.bgcolor
6882 cfg.charoff=this.charoff
6885 cfg.colspan=this.colspan
6888 cfg.headers=this.headers
6891 cfg.height=this.height
6894 cfg.nowrap=this.nowrap
6897 cfg.rowspan=this.rowspan
6900 cfg.scope=this.scope
6903 cfg.valign=this.valign
6906 cfg.width=this.width
6925 * @class Roo.bootstrap.TableRow
6926 * @extends Roo.bootstrap.Component
6927 * Bootstrap TableRow class
6928 * @cfg {String} cls row class
6929 * @cfg {String} align Aligns the content in a table row
6930 * @cfg {String} bgcolor Specifies a background color for a table row
6931 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6932 * @cfg {String} valign Vertical aligns the content in a table row
6935 * Create a new TableRow
6936 * @param {Object} config The config object
6939 Roo.bootstrap.TableRow = function(config){
6940 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6943 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6951 getAutoCreate : function(){
6952 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6962 cfg.align = this.align;
6965 cfg.bgcolor = this.bgcolor;
6968 cfg.charoff = this.charoff;
6971 cfg.valign = this.valign;
6989 * @class Roo.bootstrap.TableBody
6990 * @extends Roo.bootstrap.Component
6991 * Bootstrap TableBody class
6992 * @cfg {String} cls element class
6993 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6994 * @cfg {String} align Aligns the content inside the element
6995 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6996 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6999 * Create a new TableBody
7000 * @param {Object} config The config object
7003 Roo.bootstrap.TableBody = function(config){
7004 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7007 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7015 getAutoCreate : function(){
7016 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7030 cfg.align = this.align;
7033 cfg.charoff = this.charoff;
7036 cfg.valign = this.valign;
7043 // initEvents : function()
7050 // this.store = Roo.factory(this.store, Roo.data);
7051 // this.store.on('load', this.onLoad, this);
7053 // this.store.load();
7057 // onLoad: function ()
7059 // this.fireEvent('load', this);
7069 * Ext JS Library 1.1.1
7070 * Copyright(c) 2006-2007, Ext JS, LLC.
7072 * Originally Released Under LGPL - original licence link has changed is not relivant.
7075 * <script type="text/javascript">
7078 // as we use this in bootstrap.
7079 Roo.namespace('Roo.form');
7081 * @class Roo.form.Action
7082 * Internal Class used to handle form actions
7084 * @param {Roo.form.BasicForm} el The form element or its id
7085 * @param {Object} config Configuration options
7090 // define the action interface
7091 Roo.form.Action = function(form, options){
7093 this.options = options || {};
7096 * Client Validation Failed
7099 Roo.form.Action.CLIENT_INVALID = 'client';
7101 * Server Validation Failed
7104 Roo.form.Action.SERVER_INVALID = 'server';
7106 * Connect to Server Failed
7109 Roo.form.Action.CONNECT_FAILURE = 'connect';
7111 * Reading Data from Server Failed
7114 Roo.form.Action.LOAD_FAILURE = 'load';
7116 Roo.form.Action.prototype = {
7118 failureType : undefined,
7119 response : undefined,
7123 run : function(options){
7128 success : function(response){
7133 handleResponse : function(response){
7137 // default connection failure
7138 failure : function(response){
7140 this.response = response;
7141 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7142 this.form.afterAction(this, false);
7145 processResponse : function(response){
7146 this.response = response;
7147 if(!response.responseText){
7150 this.result = this.handleResponse(response);
7154 // utility functions used internally
7155 getUrl : function(appendParams){
7156 var url = this.options.url || this.form.url || this.form.el.dom.action;
7158 var p = this.getParams();
7160 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7166 getMethod : function(){
7167 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7170 getParams : function(){
7171 var bp = this.form.baseParams;
7172 var p = this.options.params;
7174 if(typeof p == "object"){
7175 p = Roo.urlEncode(Roo.applyIf(p, bp));
7176 }else if(typeof p == 'string' && bp){
7177 p += '&' + Roo.urlEncode(bp);
7180 p = Roo.urlEncode(bp);
7185 createCallback : function(){
7187 success: this.success,
7188 failure: this.failure,
7190 timeout: (this.form.timeout*1000),
7191 upload: this.form.fileUpload ? this.success : undefined
7196 Roo.form.Action.Submit = function(form, options){
7197 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7200 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7203 haveProgress : false,
7204 uploadComplete : false,
7206 // uploadProgress indicator.
7207 uploadProgress : function()
7209 if (!this.form.progressUrl) {
7213 if (!this.haveProgress) {
7214 Roo.MessageBox.progress("Uploading", "Uploading");
7216 if (this.uploadComplete) {
7217 Roo.MessageBox.hide();
7221 this.haveProgress = true;
7223 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7225 var c = new Roo.data.Connection();
7227 url : this.form.progressUrl,
7232 success : function(req){
7233 //console.log(data);
7237 rdata = Roo.decode(req.responseText)
7239 Roo.log("Invalid data from server..");
7243 if (!rdata || !rdata.success) {
7245 Roo.MessageBox.alert(Roo.encode(rdata));
7248 var data = rdata.data;
7250 if (this.uploadComplete) {
7251 Roo.MessageBox.hide();
7256 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7257 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7260 this.uploadProgress.defer(2000,this);
7263 failure: function(data) {
7264 Roo.log('progress url failed ');
7275 // run get Values on the form, so it syncs any secondary forms.
7276 this.form.getValues();
7278 var o = this.options;
7279 var method = this.getMethod();
7280 var isPost = method == 'POST';
7281 if(o.clientValidation === false || this.form.isValid()){
7283 if (this.form.progressUrl) {
7284 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7285 (new Date() * 1) + '' + Math.random());
7290 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7291 form:this.form.el.dom,
7292 url:this.getUrl(!isPost),
7294 params:isPost ? this.getParams() : null,
7295 isUpload: this.form.fileUpload
7298 this.uploadProgress();
7300 }else if (o.clientValidation !== false){ // client validation failed
7301 this.failureType = Roo.form.Action.CLIENT_INVALID;
7302 this.form.afterAction(this, false);
7306 success : function(response)
7308 this.uploadComplete= true;
7309 if (this.haveProgress) {
7310 Roo.MessageBox.hide();
7314 var result = this.processResponse(response);
7315 if(result === true || result.success){
7316 this.form.afterAction(this, true);
7320 this.form.markInvalid(result.errors);
7321 this.failureType = Roo.form.Action.SERVER_INVALID;
7323 this.form.afterAction(this, false);
7325 failure : function(response)
7327 this.uploadComplete= true;
7328 if (this.haveProgress) {
7329 Roo.MessageBox.hide();
7332 this.response = response;
7333 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7334 this.form.afterAction(this, false);
7337 handleResponse : function(response){
7338 if(this.form.errorReader){
7339 var rs = this.form.errorReader.read(response);
7342 for(var i = 0, len = rs.records.length; i < len; i++) {
7343 var r = rs.records[i];
7347 if(errors.length < 1){
7351 success : rs.success,
7357 ret = Roo.decode(response.responseText);
7361 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7371 Roo.form.Action.Load = function(form, options){
7372 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7373 this.reader = this.form.reader;
7376 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7381 Roo.Ajax.request(Roo.apply(
7382 this.createCallback(), {
7383 method:this.getMethod(),
7384 url:this.getUrl(false),
7385 params:this.getParams()
7389 success : function(response){
7391 var result = this.processResponse(response);
7392 if(result === true || !result.success || !result.data){
7393 this.failureType = Roo.form.Action.LOAD_FAILURE;
7394 this.form.afterAction(this, false);
7397 this.form.clearInvalid();
7398 this.form.setValues(result.data);
7399 this.form.afterAction(this, true);
7402 handleResponse : function(response){
7403 if(this.form.reader){
7404 var rs = this.form.reader.read(response);
7405 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7407 success : rs.success,
7411 return Roo.decode(response.responseText);
7415 Roo.form.Action.ACTION_TYPES = {
7416 'load' : Roo.form.Action.Load,
7417 'submit' : Roo.form.Action.Submit
7426 * @class Roo.bootstrap.Form
7427 * @extends Roo.bootstrap.Component
7428 * Bootstrap Form class
7429 * @cfg {String} method GET | POST (default POST)
7430 * @cfg {String} labelAlign top | left (default top)
7431 * @cfg {String} align left | right - for navbars
7432 * @cfg {Boolean} loadMask load mask when submit (default true)
7437 * @param {Object} config The config object
7441 Roo.bootstrap.Form = function(config){
7442 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7445 * @event clientvalidation
7446 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7447 * @param {Form} this
7448 * @param {Boolean} valid true if the form has passed client-side validation
7450 clientvalidation: true,
7452 * @event beforeaction
7453 * Fires before any action is performed. Return false to cancel the action.
7454 * @param {Form} this
7455 * @param {Action} action The action to be performed
7459 * @event actionfailed
7460 * Fires when an action fails.
7461 * @param {Form} this
7462 * @param {Action} action The action that failed
7464 actionfailed : true,
7466 * @event actioncomplete
7467 * Fires when an action is completed.
7468 * @param {Form} this
7469 * @param {Action} action The action that completed
7471 actioncomplete : true
7476 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7479 * @cfg {String} method
7480 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7485 * The URL to use for form actions if one isn't supplied in the action options.
7488 * @cfg {Boolean} fileUpload
7489 * Set to true if this form is a file upload.
7493 * @cfg {Object} baseParams
7494 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7498 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7502 * @cfg {Sting} align (left|right) for navbar forms
7507 activeAction : null,
7510 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7511 * element by passing it or its id or mask the form itself by passing in true.
7514 waitMsgTarget : false,
7518 getAutoCreate : function(){
7522 method : this.method || 'POST',
7523 id : this.id || Roo.id(),
7526 if (this.parent().xtype.match(/^Nav/)) {
7527 cfg.cls = 'navbar-form navbar-' + this.align;
7531 if (this.labelAlign == 'left' ) {
7532 cfg.cls += ' form-horizontal';
7538 initEvents : function()
7540 this.el.on('submit', this.onSubmit, this);
7541 // this was added as random key presses on the form where triggering form submit.
7542 this.el.on('keypress', function(e) {
7543 if (e.getCharCode() != 13) {
7546 // we might need to allow it for textareas.. and some other items.
7547 // check e.getTarget().
7549 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7553 Roo.log("keypress blocked");
7561 onSubmit : function(e){
7566 * Returns true if client-side validation on the form is successful.
7569 isValid : function(){
7570 var items = this.getItems();
7572 items.each(function(f){
7581 * Returns true if any fields in this form have changed since their original load.
7584 isDirty : function(){
7586 var items = this.getItems();
7587 items.each(function(f){
7597 * Performs a predefined action (submit or load) or custom actions you define on this form.
7598 * @param {String} actionName The name of the action type
7599 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7600 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7601 * accept other config options):
7603 Property Type Description
7604 ---------------- --------------- ----------------------------------------------------------------------------------
7605 url String The url for the action (defaults to the form's url)
7606 method String The form method to use (defaults to the form's method, or POST if not defined)
7607 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7608 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7609 validate the form on the client (defaults to false)
7611 * @return {BasicForm} this
7613 doAction : function(action, options){
7614 if(typeof action == 'string'){
7615 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7617 if(this.fireEvent('beforeaction', this, action) !== false){
7618 this.beforeAction(action);
7619 action.run.defer(100, action);
7625 beforeAction : function(action){
7626 var o = action.options;
7629 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7631 // not really supported yet.. ??
7633 //if(this.waitMsgTarget === true){
7634 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7635 //}else if(this.waitMsgTarget){
7636 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7637 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7639 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7645 afterAction : function(action, success){
7646 this.activeAction = null;
7647 var o = action.options;
7649 //if(this.waitMsgTarget === true){
7651 //}else if(this.waitMsgTarget){
7652 // this.waitMsgTarget.unmask();
7654 // Roo.MessageBox.updateProgress(1);
7655 // Roo.MessageBox.hide();
7662 Roo.callback(o.success, o.scope, [this, action]);
7663 this.fireEvent('actioncomplete', this, action);
7667 // failure condition..
7668 // we have a scenario where updates need confirming.
7669 // eg. if a locking scenario exists..
7670 // we look for { errors : { needs_confirm : true }} in the response.
7672 (typeof(action.result) != 'undefined') &&
7673 (typeof(action.result.errors) != 'undefined') &&
7674 (typeof(action.result.errors.needs_confirm) != 'undefined')
7677 Roo.log("not supported yet");
7680 Roo.MessageBox.confirm(
7681 "Change requires confirmation",
7682 action.result.errorMsg,
7687 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7697 Roo.callback(o.failure, o.scope, [this, action]);
7698 // show an error message if no failed handler is set..
7699 if (!this.hasListener('actionfailed')) {
7700 Roo.log("need to add dialog support");
7702 Roo.MessageBox.alert("Error",
7703 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7704 action.result.errorMsg :
7705 "Saving Failed, please check your entries or try again"
7710 this.fireEvent('actionfailed', this, action);
7715 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7716 * @param {String} id The value to search for
7719 findField : function(id){
7720 var items = this.getItems();
7721 var field = items.get(id);
7723 items.each(function(f){
7724 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7731 return field || null;
7734 * Mark fields in this form invalid in bulk.
7735 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7736 * @return {BasicForm} this
7738 markInvalid : function(errors){
7739 if(errors instanceof Array){
7740 for(var i = 0, len = errors.length; i < len; i++){
7741 var fieldError = errors[i];
7742 var f = this.findField(fieldError.id);
7744 f.markInvalid(fieldError.msg);
7750 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7751 field.markInvalid(errors[id]);
7755 //Roo.each(this.childForms || [], function (f) {
7756 // f.markInvalid(errors);
7763 * Set values for fields in this form in bulk.
7764 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7765 * @return {BasicForm} this
7767 setValues : function(values){
7768 if(values instanceof Array){ // array of objects
7769 for(var i = 0, len = values.length; i < len; i++){
7771 var f = this.findField(v.id);
7773 f.setValue(v.value);
7774 if(this.trackResetOnLoad){
7775 f.originalValue = f.getValue();
7779 }else{ // object hash
7782 if(typeof values[id] != 'function' && (field = this.findField(id))){
7784 if (field.setFromData &&
7786 field.displayField &&
7787 // combos' with local stores can
7788 // be queried via setValue()
7789 // to set their value..
7790 (field.store && !field.store.isLocal)
7794 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7795 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7796 field.setFromData(sd);
7799 field.setValue(values[id]);
7803 if(this.trackResetOnLoad){
7804 field.originalValue = field.getValue();
7810 //Roo.each(this.childForms || [], function (f) {
7811 // f.setValues(values);
7818 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7819 * they are returned as an array.
7820 * @param {Boolean} asString
7823 getValues : function(asString){
7824 //if (this.childForms) {
7825 // copy values from the child forms
7826 // Roo.each(this.childForms, function (f) {
7827 // this.setValues(f.getValues());
7833 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7834 if(asString === true){
7837 return Roo.urlDecode(fs);
7841 * Returns the fields in this form as an object with key/value pairs.
7842 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7845 getFieldValues : function(with_hidden)
7847 var items = this.getItems();
7849 items.each(function(f){
7853 var v = f.getValue();
7854 if (f.inputType =='radio') {
7855 if (typeof(ret[f.getName()]) == 'undefined') {
7856 ret[f.getName()] = ''; // empty..
7859 if (!f.el.dom.checked) {
7867 // not sure if this supported any more..
7868 if ((typeof(v) == 'object') && f.getRawValue) {
7869 v = f.getRawValue() ; // dates..
7871 // combo boxes where name != hiddenName...
7872 if (f.name != f.getName()) {
7873 ret[f.name] = f.getRawValue();
7875 ret[f.getName()] = v;
7882 * Clears all invalid messages in this form.
7883 * @return {BasicForm} this
7885 clearInvalid : function(){
7886 var items = this.getItems();
7888 items.each(function(f){
7899 * @return {BasicForm} this
7902 var items = this.getItems();
7903 items.each(function(f){
7907 Roo.each(this.childForms || [], function (f) {
7914 getItems : function()
7916 var r=new Roo.util.MixedCollection(false, function(o){
7917 return o.id || (o.id = Roo.id());
7919 var iter = function(el) {
7926 Roo.each(el.items,function(e) {
7944 * Ext JS Library 1.1.1
7945 * Copyright(c) 2006-2007, Ext JS, LLC.
7947 * Originally Released Under LGPL - original licence link has changed is not relivant.
7950 * <script type="text/javascript">
7953 * @class Roo.form.VTypes
7954 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7957 Roo.form.VTypes = function(){
7958 // closure these in so they are only created once.
7959 var alpha = /^[a-zA-Z_]+$/;
7960 var alphanum = /^[a-zA-Z0-9_]+$/;
7961 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7962 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7964 // All these messages and functions are configurable
7967 * The function used to validate email addresses
7968 * @param {String} value The email address
7970 'email' : function(v){
7971 return email.test(v);
7974 * The error text to display when the email validation function returns false
7977 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7979 * The keystroke filter mask to be applied on email input
7982 'emailMask' : /[a-z0-9_\.\-@]/i,
7985 * The function used to validate URLs
7986 * @param {String} value The URL
7988 'url' : function(v){
7992 * The error text to display when the url validation function returns false
7995 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7998 * The function used to validate alpha values
7999 * @param {String} value The value
8001 'alpha' : function(v){
8002 return alpha.test(v);
8005 * The error text to display when the alpha validation function returns false
8008 'alphaText' : 'This field should only contain letters and _',
8010 * The keystroke filter mask to be applied on alpha input
8013 'alphaMask' : /[a-z_]/i,
8016 * The function used to validate alphanumeric values
8017 * @param {String} value The value
8019 'alphanum' : function(v){
8020 return alphanum.test(v);
8023 * The error text to display when the alphanumeric validation function returns false
8026 'alphanumText' : 'This field should only contain letters, numbers and _',
8028 * The keystroke filter mask to be applied on alphanumeric input
8031 'alphanumMask' : /[a-z0-9_]/i
8041 * @class Roo.bootstrap.Input
8042 * @extends Roo.bootstrap.Component
8043 * Bootstrap Input class
8044 * @cfg {Boolean} disabled is it disabled
8045 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8046 * @cfg {String} name name of the input
8047 * @cfg {string} fieldLabel - the label associated
8048 * @cfg {string} placeholder - placeholder to put in text.
8049 * @cfg {string} before - input group add on before
8050 * @cfg {string} after - input group add on after
8051 * @cfg {string} size - (lg|sm) or leave empty..
8052 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8053 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8054 * @cfg {Number} md colspan out of 12 for computer-sized screens
8055 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8056 * @cfg {string} value default value of the input
8057 * @cfg {Number} labelWidth set the width of label (0-12)
8058 * @cfg {String} labelAlign (top|left)
8059 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8060 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8061 * @cfg {String} indicatorpos (left|right) default left
8063 * @cfg {String} align (left|center|right) Default left
8064 * @cfg {Boolean} forceFeedback (true|false) Default false
8070 * Create a new Input
8071 * @param {Object} config The config object
8074 Roo.bootstrap.Input = function(config){
8075 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8080 * Fires when this field receives input focus.
8081 * @param {Roo.form.Field} this
8086 * Fires when this field loses input focus.
8087 * @param {Roo.form.Field} this
8092 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8093 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8094 * @param {Roo.form.Field} this
8095 * @param {Roo.EventObject} e The event object
8100 * Fires just before the field blurs if the field value has changed.
8101 * @param {Roo.form.Field} this
8102 * @param {Mixed} newValue The new value
8103 * @param {Mixed} oldValue The original value
8108 * Fires after the field has been marked as invalid.
8109 * @param {Roo.form.Field} this
8110 * @param {String} msg The validation message
8115 * Fires after the field has been validated with no errors.
8116 * @param {Roo.form.Field} this
8121 * Fires after the key up
8122 * @param {Roo.form.Field} this
8123 * @param {Roo.EventObject} e The event Object
8129 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8131 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8132 automatic validation (defaults to "keyup").
8134 validationEvent : "keyup",
8136 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8138 validateOnBlur : true,
8140 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8142 validationDelay : 250,
8144 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8146 focusClass : "x-form-focus", // not needed???
8150 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8152 invalidClass : "has-warning",
8155 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8157 validClass : "has-success",
8160 * @cfg {Boolean} hasFeedback (true|false) default true
8165 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8167 invalidFeedbackClass : "glyphicon-warning-sign",
8170 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8172 validFeedbackClass : "glyphicon-ok",
8175 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8177 selectOnFocus : false,
8180 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8184 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8189 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8191 disableKeyFilter : false,
8194 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8198 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8202 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8204 blankText : "This field is required",
8207 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8211 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8213 maxLength : Number.MAX_VALUE,
8215 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8217 minLengthText : "The minimum length for this field is {0}",
8219 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8221 maxLengthText : "The maximum length for this field is {0}",
8225 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8226 * If available, this function will be called only after the basic validators all return true, and will be passed the
8227 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8231 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8232 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8233 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8237 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8241 autocomplete: false,
8260 formatedValue : false,
8261 forceFeedback : false,
8263 indicatorpos : 'left',
8265 parentLabelAlign : function()
8268 while (parent.parent()) {
8269 parent = parent.parent();
8270 if (typeof(parent.labelAlign) !='undefined') {
8271 return parent.labelAlign;
8278 getAutoCreate : function()
8280 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8286 if(this.inputType != 'hidden'){
8287 cfg.cls = 'form-group' //input-group
8293 type : this.inputType,
8295 cls : 'form-control',
8296 placeholder : this.placeholder || '',
8297 autocomplete : this.autocomplete || 'new-password'
8301 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8304 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8305 input.maxLength = this.maxLength;
8308 if (this.disabled) {
8309 input.disabled=true;
8312 if (this.readOnly) {
8313 input.readonly=true;
8317 input.name = this.name;
8321 input.cls += ' input-' + this.size;
8325 ['xs','sm','md','lg'].map(function(size){
8326 if (settings[size]) {
8327 cfg.cls += ' col-' + size + '-' + settings[size];
8331 var inputblock = input;
8335 cls: 'glyphicon form-control-feedback'
8338 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8341 cls : 'has-feedback',
8349 if (this.before || this.after) {
8352 cls : 'input-group',
8356 if (this.before && typeof(this.before) == 'string') {
8358 inputblock.cn.push({
8360 cls : 'roo-input-before input-group-addon',
8364 if (this.before && typeof(this.before) == 'object') {
8365 this.before = Roo.factory(this.before);
8367 inputblock.cn.push({
8369 cls : 'roo-input-before input-group-' +
8370 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8374 inputblock.cn.push(input);
8376 if (this.after && typeof(this.after) == 'string') {
8377 inputblock.cn.push({
8379 cls : 'roo-input-after input-group-addon',
8383 if (this.after && typeof(this.after) == 'object') {
8384 this.after = Roo.factory(this.after);
8386 inputblock.cn.push({
8388 cls : 'roo-input-after input-group-' +
8389 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8393 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8394 inputblock.cls += ' has-feedback';
8395 inputblock.cn.push(feedback);
8399 if (align ==='left' && this.fieldLabel.length) {
8404 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8405 tooltip : 'This field is required'
8410 cls : 'control-label col-sm-' + this.labelWidth,
8411 html : this.fieldLabel
8415 cls : "col-sm-" + (12 - this.labelWidth),
8423 if(this.indicatorpos == 'right'){
8428 cls : 'control-label col-sm-' + this.labelWidth,
8429 html : this.fieldLabel
8434 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8435 tooltip : 'This field is required'
8438 cls : "col-sm-" + (12 - this.labelWidth),
8447 } else if ( this.fieldLabel.length) {
8452 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8453 tooltip : 'This field is required'
8457 //cls : 'input-group-addon',
8458 html : this.fieldLabel
8466 if(this.indicatorpos == 'right'){
8471 //cls : 'input-group-addon',
8472 html : this.fieldLabel
8477 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8478 tooltip : 'This field is required'
8498 if (this.parentType === 'Navbar' && this.parent().bar) {
8499 cfg.cls += ' navbar-form';
8502 if (this.parentType === 'NavGroup') {
8503 cfg.cls += ' navbar-form';
8511 * return the real input element.
8513 inputEl: function ()
8515 return this.el.select('input.form-control',true).first();
8518 tooltipEl : function()
8520 return this.inputEl();
8523 indicatorEl : function()
8525 var indicator = this.el.select('i.roo-required-indicator',true).first();
8535 setDisabled : function(v)
8537 var i = this.inputEl().dom;
8539 i.removeAttribute('disabled');
8543 i.setAttribute('disabled','true');
8545 initEvents : function()
8548 this.inputEl().on("keydown" , this.fireKey, this);
8549 this.inputEl().on("focus", this.onFocus, this);
8550 this.inputEl().on("blur", this.onBlur, this);
8552 this.inputEl().relayEvent('keyup', this);
8554 this.indicator = this.indicatorEl();
8557 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8558 this.indicator.hide();
8561 // reference to original value for reset
8562 this.originalValue = this.getValue();
8563 //Roo.form.TextField.superclass.initEvents.call(this);
8564 if(this.validationEvent == 'keyup'){
8565 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8566 this.inputEl().on('keyup', this.filterValidation, this);
8568 else if(this.validationEvent !== false){
8569 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8572 if(this.selectOnFocus){
8573 this.on("focus", this.preFocus, this);
8576 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8577 this.inputEl().on("keypress", this.filterKeys, this);
8579 this.inputEl().relayEvent('keypress', this);
8582 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8583 this.el.on("click", this.autoSize, this);
8586 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8587 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8590 if (typeof(this.before) == 'object') {
8591 this.before.render(this.el.select('.roo-input-before',true).first());
8593 if (typeof(this.after) == 'object') {
8594 this.after.render(this.el.select('.roo-input-after',true).first());
8599 filterValidation : function(e){
8600 if(!e.isNavKeyPress()){
8601 this.validationTask.delay(this.validationDelay);
8605 * Validates the field value
8606 * @return {Boolean} True if the value is valid, else false
8608 validate : function(){
8609 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8610 if(this.disabled || this.validateValue(this.getRawValue())){
8621 * Validates a value according to the field's validation rules and marks the field as invalid
8622 * if the validation fails
8623 * @param {Mixed} value The value to validate
8624 * @return {Boolean} True if the value is valid, else false
8626 validateValue : function(value){
8627 if(value.length < 1) { // if it's blank
8628 if(this.allowBlank){
8634 if(value.length < this.minLength){
8637 if(value.length > this.maxLength){
8641 var vt = Roo.form.VTypes;
8642 if(!vt[this.vtype](value, this)){
8646 if(typeof this.validator == "function"){
8647 var msg = this.validator(value);
8653 if(this.regex && !this.regex.test(value)){
8663 fireKey : function(e){
8664 //Roo.log('field ' + e.getKey());
8665 if(e.isNavKeyPress()){
8666 this.fireEvent("specialkey", this, e);
8669 focus : function (selectText){
8671 this.inputEl().focus();
8672 if(selectText === true){
8673 this.inputEl().dom.select();
8679 onFocus : function(){
8680 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8681 // this.el.addClass(this.focusClass);
8684 this.hasFocus = true;
8685 this.startValue = this.getValue();
8686 this.fireEvent("focus", this);
8690 beforeBlur : Roo.emptyFn,
8694 onBlur : function(){
8696 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8697 //this.el.removeClass(this.focusClass);
8699 this.hasFocus = false;
8700 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8703 var v = this.getValue();
8704 if(String(v) !== String(this.startValue)){
8705 this.fireEvent('change', this, v, this.startValue);
8707 this.fireEvent("blur", this);
8711 * Resets the current field value to the originally loaded value and clears any validation messages
8714 this.setValue(this.originalValue);
8718 * Returns the name of the field
8719 * @return {Mixed} name The name field
8721 getName: function(){
8725 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8726 * @return {Mixed} value The field value
8728 getValue : function(){
8730 var v = this.inputEl().getValue();
8735 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8736 * @return {Mixed} value The field value
8738 getRawValue : function(){
8739 var v = this.inputEl().getValue();
8745 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8746 * @param {Mixed} value The value to set
8748 setRawValue : function(v){
8749 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8752 selectText : function(start, end){
8753 var v = this.getRawValue();
8755 start = start === undefined ? 0 : start;
8756 end = end === undefined ? v.length : end;
8757 var d = this.inputEl().dom;
8758 if(d.setSelectionRange){
8759 d.setSelectionRange(start, end);
8760 }else if(d.createTextRange){
8761 var range = d.createTextRange();
8762 range.moveStart("character", start);
8763 range.moveEnd("character", v.length-end);
8770 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8771 * @param {Mixed} value The value to set
8773 setValue : function(v){
8776 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8782 processValue : function(value){
8783 if(this.stripCharsRe){
8784 var newValue = value.replace(this.stripCharsRe, '');
8785 if(newValue !== value){
8786 this.setRawValue(newValue);
8793 preFocus : function(){
8795 if(this.selectOnFocus){
8796 this.inputEl().dom.select();
8799 filterKeys : function(e){
8801 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8804 var c = e.getCharCode(), cc = String.fromCharCode(c);
8805 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8808 if(!this.maskRe.test(cc)){
8813 * Clear any invalid styles/messages for this field
8815 clearInvalid : function(){
8817 if(!this.el || this.preventMark){ // not rendered
8822 this.indicator.hide();
8825 this.el.removeClass(this.invalidClass);
8827 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8829 var feedback = this.el.select('.form-control-feedback', true).first();
8832 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8837 this.fireEvent('valid', this);
8841 * Mark this field as valid
8843 markValid : function()
8845 if(!this.el || this.preventMark){ // not rendered
8849 this.el.removeClass([this.invalidClass, this.validClass]);
8851 var feedback = this.el.select('.form-control-feedback', true).first();
8854 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8861 if(this.allowBlank && !this.getRawValue().length){
8866 this.indicator.hide();
8869 this.el.addClass(this.validClass);
8871 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8873 var feedback = this.el.select('.form-control-feedback', true).first();
8876 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8877 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8882 this.fireEvent('valid', this);
8886 * Mark this field as invalid
8887 * @param {String} msg The validation message
8889 markInvalid : function(msg)
8891 if(!this.el || this.preventMark){ // not rendered
8895 this.el.removeClass([this.invalidClass, this.validClass]);
8897 var feedback = this.el.select('.form-control-feedback', true).first();
8900 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8907 if(this.allowBlank && !this.getRawValue().length){
8912 this.indicator.show();
8915 this.el.addClass(this.invalidClass);
8917 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8919 var feedback = this.el.select('.form-control-feedback', true).first();
8922 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8924 if(this.getValue().length || this.forceFeedback){
8925 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8932 this.fireEvent('invalid', this, msg);
8935 SafariOnKeyDown : function(event)
8937 // this is a workaround for a password hang bug on chrome/ webkit.
8938 if (this.inputEl().dom.type != 'password') {
8942 var isSelectAll = false;
8944 if(this.inputEl().dom.selectionEnd > 0){
8945 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8947 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8948 event.preventDefault();
8953 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
8955 event.preventDefault();
8956 // this is very hacky as keydown always get's upper case.
8958 var cc = String.fromCharCode(event.getCharCode());
8959 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8963 adjustWidth : function(tag, w){
8964 tag = tag.toLowerCase();
8965 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8966 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8970 if(tag == 'textarea'){
8973 }else if(Roo.isOpera){
8977 if(tag == 'textarea'){
8996 * @class Roo.bootstrap.TextArea
8997 * @extends Roo.bootstrap.Input
8998 * Bootstrap TextArea class
8999 * @cfg {Number} cols Specifies the visible width of a text area
9000 * @cfg {Number} rows Specifies the visible number of lines in a text area
9001 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9002 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9003 * @cfg {string} html text
9006 * Create a new TextArea
9007 * @param {Object} config The config object
9010 Roo.bootstrap.TextArea = function(config){
9011 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9015 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9025 getAutoCreate : function(){
9027 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9038 value : this.value || '',
9039 html: this.html || '',
9040 cls : 'form-control',
9041 placeholder : this.placeholder || ''
9045 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9046 input.maxLength = this.maxLength;
9050 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9054 input.cols = this.cols;
9057 if (this.readOnly) {
9058 input.readonly = true;
9062 input.name = this.name;
9066 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9070 ['xs','sm','md','lg'].map(function(size){
9071 if (settings[size]) {
9072 cfg.cls += ' col-' + size + '-' + settings[size];
9076 var inputblock = input;
9078 if(this.hasFeedback && !this.allowBlank){
9082 cls: 'glyphicon form-control-feedback'
9086 cls : 'has-feedback',
9095 if (this.before || this.after) {
9098 cls : 'input-group',
9102 inputblock.cn.push({
9104 cls : 'input-group-addon',
9109 inputblock.cn.push(input);
9111 if(this.hasFeedback && !this.allowBlank){
9112 inputblock.cls += ' has-feedback';
9113 inputblock.cn.push(feedback);
9117 inputblock.cn.push({
9119 cls : 'input-group-addon',
9126 if (align ==='left' && this.fieldLabel.length) {
9127 // Roo.log("left and has label");
9133 cls : 'control-label col-sm-' + this.labelWidth,
9134 html : this.fieldLabel
9138 cls : "col-sm-" + (12 - this.labelWidth),
9145 } else if ( this.fieldLabel.length) {
9146 // Roo.log(" label");
9151 //cls : 'input-group-addon',
9152 html : this.fieldLabel
9162 // Roo.log(" no label && no align");
9172 if (this.disabled) {
9173 input.disabled=true;
9180 * return the real textarea element.
9182 inputEl: function ()
9184 return this.el.select('textarea.form-control',true).first();
9188 * Clear any invalid styles/messages for this field
9190 clearInvalid : function()
9193 if(!this.el || this.preventMark){ // not rendered
9197 var label = this.el.select('label', true).first();
9198 var icon = this.el.select('i.fa-star', true).first();
9204 this.el.removeClass(this.invalidClass);
9206 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9208 var feedback = this.el.select('.form-control-feedback', true).first();
9211 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9216 this.fireEvent('valid', this);
9220 * Mark this field as valid
9222 markValid : function()
9224 if(!this.el || this.preventMark){ // not rendered
9228 this.el.removeClass([this.invalidClass, this.validClass]);
9230 var feedback = this.el.select('.form-control-feedback', true).first();
9233 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9236 if(this.disabled || this.allowBlank){
9240 var label = this.el.select('label', true).first();
9241 var icon = this.el.select('i.fa-star', true).first();
9247 this.el.addClass(this.validClass);
9249 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9251 var feedback = this.el.select('.form-control-feedback', true).first();
9254 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9255 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9260 this.fireEvent('valid', this);
9264 * Mark this field as invalid
9265 * @param {String} msg The validation message
9267 markInvalid : function(msg)
9269 if(!this.el || this.preventMark){ // not rendered
9273 this.el.removeClass([this.invalidClass, this.validClass]);
9275 var feedback = this.el.select('.form-control-feedback', true).first();
9278 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9281 if(this.disabled || this.allowBlank){
9285 var label = this.el.select('label', true).first();
9286 var icon = this.el.select('i.fa-star', true).first();
9288 if(!this.getValue().length && label && !icon){
9289 this.el.createChild({
9291 cls : 'text-danger fa fa-lg fa-star',
9292 tooltip : 'This field is required',
9293 style : 'margin-right:5px;'
9297 this.el.addClass(this.invalidClass);
9299 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9301 var feedback = this.el.select('.form-control-feedback', true).first();
9304 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9306 if(this.getValue().length || this.forceFeedback){
9307 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9314 this.fireEvent('invalid', this, msg);
9322 * trigger field - base class for combo..
9327 * @class Roo.bootstrap.TriggerField
9328 * @extends Roo.bootstrap.Input
9329 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9330 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9331 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9332 * for which you can provide a custom implementation. For example:
9334 var trigger = new Roo.bootstrap.TriggerField();
9335 trigger.onTriggerClick = myTriggerFn;
9336 trigger.applyTo('my-field');
9339 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9340 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9341 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
9342 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9343 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9346 * Create a new TriggerField.
9347 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9348 * to the base TextField)
9350 Roo.bootstrap.TriggerField = function(config){
9351 this.mimicing = false;
9352 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9355 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
9357 * @cfg {String} triggerClass A CSS class to apply to the trigger
9360 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9365 * @cfg {Boolean} removable (true|false) special filter default false
9369 /** @cfg {Boolean} grow @hide */
9370 /** @cfg {Number} growMin @hide */
9371 /** @cfg {Number} growMax @hide */
9377 autoSize: Roo.emptyFn,
9384 actionMode : 'wrap',
9389 getAutoCreate : function(){
9391 var align = this.labelAlign || this.parentLabelAlign();
9396 cls: 'form-group' //input-group
9403 type : this.inputType,
9404 cls : 'form-control',
9405 autocomplete: 'new-password',
9406 placeholder : this.placeholder || ''
9410 input.name = this.name;
9413 input.cls += ' input-' + this.size;
9416 if (this.disabled) {
9417 input.disabled=true;
9420 var inputblock = input;
9422 if(this.hasFeedback && !this.allowBlank){
9426 cls: 'glyphicon form-control-feedback'
9429 if(this.removable && !this.editable && !this.tickable){
9431 cls : 'has-feedback',
9437 cls : 'roo-combo-removable-btn close'
9444 cls : 'has-feedback',
9453 if(this.removable && !this.editable && !this.tickable){
9455 cls : 'roo-removable',
9461 cls : 'roo-combo-removable-btn close'
9468 if (this.before || this.after) {
9471 cls : 'input-group',
9475 inputblock.cn.push({
9477 cls : 'input-group-addon',
9482 inputblock.cn.push(input);
9484 if(this.hasFeedback && !this.allowBlank){
9485 inputblock.cls += ' has-feedback';
9486 inputblock.cn.push(feedback);
9490 inputblock.cn.push({
9492 cls : 'input-group-addon',
9505 cls: 'form-hidden-field'
9519 cls: 'form-hidden-field'
9523 cls: 'roo-select2-choices',
9527 cls: 'roo-select2-search-field',
9540 cls: 'roo-select2-container input-group',
9545 // cls: 'typeahead typeahead-long dropdown-menu',
9546 // style: 'display:none'
9551 if(!this.multiple && this.showToggleBtn){
9557 if (this.caret != false) {
9560 cls: 'fa fa-' + this.caret
9567 cls : 'input-group-addon btn dropdown-toggle',
9572 cls: 'combobox-clear',
9586 combobox.cls += ' roo-select2-container-multi';
9589 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9591 // Roo.log("left and has label");
9595 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9596 tooltip : 'This field is required'
9601 cls : 'control-label col-sm-' + this.labelWidth,
9602 html : this.fieldLabel
9606 cls : "col-sm-" + (12 - this.labelWidth),
9614 if(this.indicatorpos == 'right'){
9619 cls : 'control-label col-sm-' + this.labelWidth,
9620 html : this.fieldLabel
9625 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9626 tooltip : 'This field is required'
9629 cls : "col-sm-" + (12 - this.labelWidth),
9638 } else if ( this.fieldLabel.length) {
9639 // Roo.log(" label");
9643 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9644 tooltip : 'This field is required'
9648 //cls : 'input-group-addon',
9649 html : this.fieldLabel
9657 if(this.indicatorpos == 'right'){
9662 //cls : 'input-group-addon',
9663 html : this.fieldLabel
9668 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9669 tooltip : 'This field is required'
9680 // Roo.log(" no label && no align");
9687 ['xs','sm','md','lg'].map(function(size){
9688 if (settings[size]) {
9689 cfg.cls += ' col-' + size + '-' + settings[size];
9700 onResize : function(w, h){
9701 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9702 // if(typeof w == 'number'){
9703 // var x = w - this.trigger.getWidth();
9704 // this.inputEl().setWidth(this.adjustWidth('input', x));
9705 // this.trigger.setStyle('left', x+'px');
9710 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9713 getResizeEl : function(){
9714 return this.inputEl();
9718 getPositionEl : function(){
9719 return this.inputEl();
9723 alignErrorIcon : function(){
9724 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9728 initEvents : function(){
9732 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9733 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9734 if(!this.multiple && this.showToggleBtn){
9735 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9736 if(this.hideTrigger){
9737 this.trigger.setDisplayed(false);
9739 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9743 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9746 if(this.removable && !this.editable && !this.tickable){
9747 var close = this.closeTriggerEl();
9750 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9751 close.on('click', this.removeBtnClick, this, close);
9755 //this.trigger.addClassOnOver('x-form-trigger-over');
9756 //this.trigger.addClassOnClick('x-form-trigger-click');
9759 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9763 closeTriggerEl : function()
9765 var close = this.el.select('.roo-combo-removable-btn', true).first();
9766 return close ? close : false;
9769 removeBtnClick : function(e, h, el)
9773 if(this.fireEvent("remove", this) !== false){
9775 this.fireEvent("afterremove", this)
9779 createList : function()
9781 this.list = Roo.get(document.body).createChild({
9783 cls: 'typeahead typeahead-long dropdown-menu',
9784 style: 'display:none'
9787 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9792 initTrigger : function(){
9797 onDestroy : function(){
9799 this.trigger.removeAllListeners();
9800 // this.trigger.remove();
9803 // this.wrap.remove();
9805 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9809 onFocus : function(){
9810 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9813 this.wrap.addClass('x-trigger-wrap-focus');
9814 this.mimicing = true;
9815 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9816 if(this.monitorTab){
9817 this.el.on("keydown", this.checkTab, this);
9824 checkTab : function(e){
9825 if(e.getKey() == e.TAB){
9831 onBlur : function(){
9836 mimicBlur : function(e, t){
9838 if(!this.wrap.contains(t) && this.validateBlur()){
9845 triggerBlur : function(){
9846 this.mimicing = false;
9847 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9848 if(this.monitorTab){
9849 this.el.un("keydown", this.checkTab, this);
9851 //this.wrap.removeClass('x-trigger-wrap-focus');
9852 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9856 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9857 validateBlur : function(e, t){
9862 onDisable : function(){
9863 this.inputEl().dom.disabled = true;
9864 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9866 // this.wrap.addClass('x-item-disabled');
9871 onEnable : function(){
9872 this.inputEl().dom.disabled = false;
9873 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9875 // this.el.removeClass('x-item-disabled');
9880 onShow : function(){
9881 var ae = this.getActionEl();
9884 ae.dom.style.display = '';
9885 ae.dom.style.visibility = 'visible';
9891 onHide : function(){
9892 var ae = this.getActionEl();
9893 ae.dom.style.display = 'none';
9897 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9898 * by an implementing function.
9900 * @param {EventObject} e
9902 onTriggerClick : Roo.emptyFn
9906 * Ext JS Library 1.1.1
9907 * Copyright(c) 2006-2007, Ext JS, LLC.
9909 * Originally Released Under LGPL - original licence link has changed is not relivant.
9912 * <script type="text/javascript">
9917 * @class Roo.data.SortTypes
9919 * Defines the default sorting (casting?) comparison functions used when sorting data.
9921 Roo.data.SortTypes = {
9923 * Default sort that does nothing
9924 * @param {Mixed} s The value being converted
9925 * @return {Mixed} The comparison value
9932 * The regular expression used to strip tags
9936 stripTagsRE : /<\/?[^>]+>/gi,
9939 * Strips all HTML tags to sort on text only
9940 * @param {Mixed} s The value being converted
9941 * @return {String} The comparison value
9943 asText : function(s){
9944 return String(s).replace(this.stripTagsRE, "");
9948 * Strips all HTML tags to sort on text only - Case insensitive
9949 * @param {Mixed} s The value being converted
9950 * @return {String} The comparison value
9952 asUCText : function(s){
9953 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9957 * Case insensitive string
9958 * @param {Mixed} s The value being converted
9959 * @return {String} The comparison value
9961 asUCString : function(s) {
9962 return String(s).toUpperCase();
9967 * @param {Mixed} s The value being converted
9968 * @return {Number} The comparison value
9970 asDate : function(s) {
9974 if(s instanceof Date){
9977 return Date.parse(String(s));
9982 * @param {Mixed} s The value being converted
9983 * @return {Float} The comparison value
9985 asFloat : function(s) {
9986 var val = parseFloat(String(s).replace(/,/g, ""));
9995 * @param {Mixed} s The value being converted
9996 * @return {Number} The comparison value
9998 asInt : function(s) {
9999 var val = parseInt(String(s).replace(/,/g, ""));
10007 * Ext JS Library 1.1.1
10008 * Copyright(c) 2006-2007, Ext JS, LLC.
10010 * Originally Released Under LGPL - original licence link has changed is not relivant.
10013 * <script type="text/javascript">
10017 * @class Roo.data.Record
10018 * Instances of this class encapsulate both record <em>definition</em> information, and record
10019 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10020 * to access Records cached in an {@link Roo.data.Store} object.<br>
10022 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10023 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10026 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10028 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10029 * {@link #create}. The parameters are the same.
10030 * @param {Array} data An associative Array of data values keyed by the field name.
10031 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10032 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10033 * not specified an integer id is generated.
10035 Roo.data.Record = function(data, id){
10036 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10041 * Generate a constructor for a specific record layout.
10042 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10043 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10044 * Each field definition object may contain the following properties: <ul>
10045 * <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,
10046 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10047 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10048 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10049 * is being used, then this is a string containing the javascript expression to reference the data relative to
10050 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10051 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10052 * this may be omitted.</p></li>
10053 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10054 * <ul><li>auto (Default, implies no conversion)</li>
10059 * <li>date</li></ul></p></li>
10060 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10061 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10062 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10063 * by the Reader into an object that will be stored in the Record. It is passed the
10064 * following parameters:<ul>
10065 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10067 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10069 * <br>usage:<br><pre><code>
10070 var TopicRecord = Roo.data.Record.create(
10071 {name: 'title', mapping: 'topic_title'},
10072 {name: 'author', mapping: 'username'},
10073 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10074 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10075 {name: 'lastPoster', mapping: 'user2'},
10076 {name: 'excerpt', mapping: 'post_text'}
10079 var myNewRecord = new TopicRecord({
10080 title: 'Do my job please',
10083 lastPost: new Date(),
10084 lastPoster: 'Animal',
10085 excerpt: 'No way dude!'
10087 myStore.add(myNewRecord);
10092 Roo.data.Record.create = function(o){
10093 var f = function(){
10094 f.superclass.constructor.apply(this, arguments);
10096 Roo.extend(f, Roo.data.Record);
10097 var p = f.prototype;
10098 p.fields = new Roo.util.MixedCollection(false, function(field){
10101 for(var i = 0, len = o.length; i < len; i++){
10102 p.fields.add(new Roo.data.Field(o[i]));
10104 f.getField = function(name){
10105 return p.fields.get(name);
10110 Roo.data.Record.AUTO_ID = 1000;
10111 Roo.data.Record.EDIT = 'edit';
10112 Roo.data.Record.REJECT = 'reject';
10113 Roo.data.Record.COMMIT = 'commit';
10115 Roo.data.Record.prototype = {
10117 * Readonly flag - true if this record has been modified.
10126 join : function(store){
10127 this.store = store;
10131 * Set the named field to the specified value.
10132 * @param {String} name The name of the field to set.
10133 * @param {Object} value The value to set the field to.
10135 set : function(name, value){
10136 if(this.data[name] == value){
10140 if(!this.modified){
10141 this.modified = {};
10143 if(typeof this.modified[name] == 'undefined'){
10144 this.modified[name] = this.data[name];
10146 this.data[name] = value;
10147 if(!this.editing && this.store){
10148 this.store.afterEdit(this);
10153 * Get the value of the named field.
10154 * @param {String} name The name of the field to get the value of.
10155 * @return {Object} The value of the field.
10157 get : function(name){
10158 return this.data[name];
10162 beginEdit : function(){
10163 this.editing = true;
10164 this.modified = {};
10168 cancelEdit : function(){
10169 this.editing = false;
10170 delete this.modified;
10174 endEdit : function(){
10175 this.editing = false;
10176 if(this.dirty && this.store){
10177 this.store.afterEdit(this);
10182 * Usually called by the {@link Roo.data.Store} which owns the Record.
10183 * Rejects all changes made to the Record since either creation, or the last commit operation.
10184 * Modified fields are reverted to their original values.
10186 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10187 * of reject operations.
10189 reject : function(){
10190 var m = this.modified;
10192 if(typeof m[n] != "function"){
10193 this.data[n] = m[n];
10196 this.dirty = false;
10197 delete this.modified;
10198 this.editing = false;
10200 this.store.afterReject(this);
10205 * Usually called by the {@link Roo.data.Store} which owns the Record.
10206 * Commits all changes made to the Record since either creation, or the last commit operation.
10208 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10209 * of commit operations.
10211 commit : function(){
10212 this.dirty = false;
10213 delete this.modified;
10214 this.editing = false;
10216 this.store.afterCommit(this);
10221 hasError : function(){
10222 return this.error != null;
10226 clearError : function(){
10231 * Creates a copy of this record.
10232 * @param {String} id (optional) A new record id if you don't want to use this record's id
10235 copy : function(newId) {
10236 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10240 * Ext JS Library 1.1.1
10241 * Copyright(c) 2006-2007, Ext JS, LLC.
10243 * Originally Released Under LGPL - original licence link has changed is not relivant.
10246 * <script type="text/javascript">
10252 * @class Roo.data.Store
10253 * @extends Roo.util.Observable
10254 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10255 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10257 * 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
10258 * has no knowledge of the format of the data returned by the Proxy.<br>
10260 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10261 * instances from the data object. These records are cached and made available through accessor functions.
10263 * Creates a new Store.
10264 * @param {Object} config A config object containing the objects needed for the Store to access data,
10265 * and read the data into Records.
10267 Roo.data.Store = function(config){
10268 this.data = new Roo.util.MixedCollection(false);
10269 this.data.getKey = function(o){
10272 this.baseParams = {};
10274 this.paramNames = {
10279 "multisort" : "_multisort"
10282 if(config && config.data){
10283 this.inlineData = config.data;
10284 delete config.data;
10287 Roo.apply(this, config);
10289 if(this.reader){ // reader passed
10290 this.reader = Roo.factory(this.reader, Roo.data);
10291 this.reader.xmodule = this.xmodule || false;
10292 if(!this.recordType){
10293 this.recordType = this.reader.recordType;
10295 if(this.reader.onMetaChange){
10296 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10300 if(this.recordType){
10301 this.fields = this.recordType.prototype.fields;
10303 this.modified = [];
10307 * @event datachanged
10308 * Fires when the data cache has changed, and a widget which is using this Store
10309 * as a Record cache should refresh its view.
10310 * @param {Store} this
10312 datachanged : true,
10314 * @event metachange
10315 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10316 * @param {Store} this
10317 * @param {Object} meta The JSON metadata
10322 * Fires when Records have been added to the Store
10323 * @param {Store} this
10324 * @param {Roo.data.Record[]} records The array of Records added
10325 * @param {Number} index The index at which the record(s) were added
10330 * Fires when a Record has been removed from the Store
10331 * @param {Store} this
10332 * @param {Roo.data.Record} record The Record that was removed
10333 * @param {Number} index The index at which the record was removed
10338 * Fires when a Record has been updated
10339 * @param {Store} this
10340 * @param {Roo.data.Record} record The Record that was updated
10341 * @param {String} operation The update operation being performed. Value may be one of:
10343 Roo.data.Record.EDIT
10344 Roo.data.Record.REJECT
10345 Roo.data.Record.COMMIT
10351 * Fires when the data cache has been cleared.
10352 * @param {Store} this
10356 * @event beforeload
10357 * Fires before a request is made for a new data object. If the beforeload handler returns false
10358 * the load action will be canceled.
10359 * @param {Store} this
10360 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10364 * @event beforeloadadd
10365 * Fires after a new set of Records has been loaded.
10366 * @param {Store} this
10367 * @param {Roo.data.Record[]} records The Records that were loaded
10368 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10370 beforeloadadd : true,
10373 * Fires after a new set of Records has been loaded, before they are added to the store.
10374 * @param {Store} this
10375 * @param {Roo.data.Record[]} records The Records that were loaded
10376 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10377 * @params {Object} return from reader
10381 * @event loadexception
10382 * Fires if an exception occurs in the Proxy during loading.
10383 * Called with the signature of the Proxy's "loadexception" event.
10384 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10387 * @param {Object} return from JsonData.reader() - success, totalRecords, records
10388 * @param {Object} load options
10389 * @param {Object} jsonData from your request (normally this contains the Exception)
10391 loadexception : true
10395 this.proxy = Roo.factory(this.proxy, Roo.data);
10396 this.proxy.xmodule = this.xmodule || false;
10397 this.relayEvents(this.proxy, ["loadexception"]);
10399 this.sortToggle = {};
10400 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10402 Roo.data.Store.superclass.constructor.call(this);
10404 if(this.inlineData){
10405 this.loadData(this.inlineData);
10406 delete this.inlineData;
10410 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10412 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
10413 * without a remote query - used by combo/forms at present.
10417 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10420 * @cfg {Array} data Inline data to be loaded when the store is initialized.
10423 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10424 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10427 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10428 * on any HTTP request
10431 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10434 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10438 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10439 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10441 remoteSort : false,
10444 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10445 * loaded or when a record is removed. (defaults to false).
10447 pruneModifiedRecords : false,
10450 lastOptions : null,
10453 * Add Records to the Store and fires the add event.
10454 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10456 add : function(records){
10457 records = [].concat(records);
10458 for(var i = 0, len = records.length; i < len; i++){
10459 records[i].join(this);
10461 var index = this.data.length;
10462 this.data.addAll(records);
10463 this.fireEvent("add", this, records, index);
10467 * Remove a Record from the Store and fires the remove event.
10468 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10470 remove : function(record){
10471 var index = this.data.indexOf(record);
10472 this.data.removeAt(index);
10473 if(this.pruneModifiedRecords){
10474 this.modified.remove(record);
10476 this.fireEvent("remove", this, record, index);
10480 * Remove all Records from the Store and fires the clear event.
10482 removeAll : function(){
10484 if(this.pruneModifiedRecords){
10485 this.modified = [];
10487 this.fireEvent("clear", this);
10491 * Inserts Records to the Store at the given index and fires the add event.
10492 * @param {Number} index The start index at which to insert the passed Records.
10493 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10495 insert : function(index, records){
10496 records = [].concat(records);
10497 for(var i = 0, len = records.length; i < len; i++){
10498 this.data.insert(index, records[i]);
10499 records[i].join(this);
10501 this.fireEvent("add", this, records, index);
10505 * Get the index within the cache of the passed Record.
10506 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10507 * @return {Number} The index of the passed Record. Returns -1 if not found.
10509 indexOf : function(record){
10510 return this.data.indexOf(record);
10514 * Get the index within the cache of the Record with the passed id.
10515 * @param {String} id The id of the Record to find.
10516 * @return {Number} The index of the Record. Returns -1 if not found.
10518 indexOfId : function(id){
10519 return this.data.indexOfKey(id);
10523 * Get the Record with the specified id.
10524 * @param {String} id The id of the Record to find.
10525 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10527 getById : function(id){
10528 return this.data.key(id);
10532 * Get the Record at the specified index.
10533 * @param {Number} index The index of the Record to find.
10534 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10536 getAt : function(index){
10537 return this.data.itemAt(index);
10541 * Returns a range of Records between specified indices.
10542 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10543 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10544 * @return {Roo.data.Record[]} An array of Records
10546 getRange : function(start, end){
10547 return this.data.getRange(start, end);
10551 storeOptions : function(o){
10552 o = Roo.apply({}, o);
10555 this.lastOptions = o;
10559 * Loads the Record cache from the configured Proxy using the configured Reader.
10561 * If using remote paging, then the first load call must specify the <em>start</em>
10562 * and <em>limit</em> properties in the options.params property to establish the initial
10563 * position within the dataset, and the number of Records to cache on each read from the Proxy.
10565 * <strong>It is important to note that for remote data sources, loading is asynchronous,
10566 * and this call will return before the new data has been loaded. Perform any post-processing
10567 * in a callback function, or in a "load" event handler.</strong>
10569 * @param {Object} options An object containing properties which control loading options:<ul>
10570 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10571 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10572 * passed the following arguments:<ul>
10573 * <li>r : Roo.data.Record[]</li>
10574 * <li>options: Options object from the load call</li>
10575 * <li>success: Boolean success indicator</li></ul></li>
10576 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10577 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10580 load : function(options){
10581 options = options || {};
10582 if(this.fireEvent("beforeload", this, options) !== false){
10583 this.storeOptions(options);
10584 var p = Roo.apply(options.params || {}, this.baseParams);
10585 // if meta was not loaded from remote source.. try requesting it.
10586 if (!this.reader.metaFromRemote) {
10587 p._requestMeta = 1;
10589 if(this.sortInfo && this.remoteSort){
10590 var pn = this.paramNames;
10591 p[pn["sort"]] = this.sortInfo.field;
10592 p[pn["dir"]] = this.sortInfo.direction;
10594 if (this.multiSort) {
10595 var pn = this.paramNames;
10596 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10599 this.proxy.load(p, this.reader, this.loadRecords, this, options);
10604 * Reloads the Record cache from the configured Proxy using the configured Reader and
10605 * the options from the last load operation performed.
10606 * @param {Object} options (optional) An object containing properties which may override the options
10607 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10608 * the most recently used options are reused).
10610 reload : function(options){
10611 this.load(Roo.applyIf(options||{}, this.lastOptions));
10615 // Called as a callback by the Reader during a load operation.
10616 loadRecords : function(o, options, success){
10617 if(!o || success === false){
10618 if(success !== false){
10619 this.fireEvent("load", this, [], options, o);
10621 if(options.callback){
10622 options.callback.call(options.scope || this, [], options, false);
10626 // if data returned failure - throw an exception.
10627 if (o.success === false) {
10628 // show a message if no listener is registered.
10629 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10630 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10632 // loadmask wil be hooked into this..
10633 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10636 var r = o.records, t = o.totalRecords || r.length;
10638 this.fireEvent("beforeloadadd", this, r, options, o);
10640 if(!options || options.add !== true){
10641 if(this.pruneModifiedRecords){
10642 this.modified = [];
10644 for(var i = 0, len = r.length; i < len; i++){
10648 this.data = this.snapshot;
10649 delete this.snapshot;
10652 this.data.addAll(r);
10653 this.totalLength = t;
10655 this.fireEvent("datachanged", this);
10657 this.totalLength = Math.max(t, this.data.length+r.length);
10660 this.fireEvent("load", this, r, options, o);
10661 if(options.callback){
10662 options.callback.call(options.scope || this, r, options, true);
10668 * Loads data from a passed data block. A Reader which understands the format of the data
10669 * must have been configured in the constructor.
10670 * @param {Object} data The data block from which to read the Records. The format of the data expected
10671 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10672 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10674 loadData : function(o, append){
10675 var r = this.reader.readRecords(o);
10676 this.loadRecords(r, {add: append}, true);
10680 * Gets the number of cached records.
10682 * <em>If using paging, this may not be the total size of the dataset. If the data object
10683 * used by the Reader contains the dataset size, then the getTotalCount() function returns
10684 * the data set size</em>
10686 getCount : function(){
10687 return this.data.length || 0;
10691 * Gets the total number of records in the dataset as returned by the server.
10693 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10694 * the dataset size</em>
10696 getTotalCount : function(){
10697 return this.totalLength || 0;
10701 * Returns the sort state of the Store as an object with two properties:
10703 field {String} The name of the field by which the Records are sorted
10704 direction {String} The sort order, "ASC" or "DESC"
10707 getSortState : function(){
10708 return this.sortInfo;
10712 applySort : function(){
10713 if(this.sortInfo && !this.remoteSort){
10714 var s = this.sortInfo, f = s.field;
10715 var st = this.fields.get(f).sortType;
10716 var fn = function(r1, r2){
10717 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10718 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10720 this.data.sort(s.direction, fn);
10721 if(this.snapshot && this.snapshot != this.data){
10722 this.snapshot.sort(s.direction, fn);
10728 * Sets the default sort column and order to be used by the next load operation.
10729 * @param {String} fieldName The name of the field to sort by.
10730 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10732 setDefaultSort : function(field, dir){
10733 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10737 * Sort the Records.
10738 * If remote sorting is used, the sort is performed on the server, and the cache is
10739 * reloaded. If local sorting is used, the cache is sorted internally.
10740 * @param {String} fieldName The name of the field to sort by.
10741 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10743 sort : function(fieldName, dir){
10744 var f = this.fields.get(fieldName);
10746 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10748 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10749 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10754 this.sortToggle[f.name] = dir;
10755 this.sortInfo = {field: f.name, direction: dir};
10756 if(!this.remoteSort){
10758 this.fireEvent("datachanged", this);
10760 this.load(this.lastOptions);
10765 * Calls the specified function for each of the Records in the cache.
10766 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10767 * Returning <em>false</em> aborts and exits the iteration.
10768 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10770 each : function(fn, scope){
10771 this.data.each(fn, scope);
10775 * Gets all records modified since the last commit. Modified records are persisted across load operations
10776 * (e.g., during paging).
10777 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10779 getModifiedRecords : function(){
10780 return this.modified;
10784 createFilterFn : function(property, value, anyMatch){
10785 if(!value.exec){ // not a regex
10786 value = String(value);
10787 if(value.length == 0){
10790 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10792 return function(r){
10793 return value.test(r.data[property]);
10798 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10799 * @param {String} property A field on your records
10800 * @param {Number} start The record index to start at (defaults to 0)
10801 * @param {Number} end The last record index to include (defaults to length - 1)
10802 * @return {Number} The sum
10804 sum : function(property, start, end){
10805 var rs = this.data.items, v = 0;
10806 start = start || 0;
10807 end = (end || end === 0) ? end : rs.length-1;
10809 for(var i = start; i <= end; i++){
10810 v += (rs[i].data[property] || 0);
10816 * Filter the records by a specified property.
10817 * @param {String} field A field on your records
10818 * @param {String/RegExp} value Either a string that the field
10819 * should start with or a RegExp to test against the field
10820 * @param {Boolean} anyMatch True to match any part not just the beginning
10822 filter : function(property, value, anyMatch){
10823 var fn = this.createFilterFn(property, value, anyMatch);
10824 return fn ? this.filterBy(fn) : this.clearFilter();
10828 * Filter by a function. The specified function will be called with each
10829 * record in this data source. If the function returns true the record is included,
10830 * otherwise it is filtered.
10831 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10832 * @param {Object} scope (optional) The scope of the function (defaults to this)
10834 filterBy : function(fn, scope){
10835 this.snapshot = this.snapshot || this.data;
10836 this.data = this.queryBy(fn, scope||this);
10837 this.fireEvent("datachanged", this);
10841 * Query the records by a specified property.
10842 * @param {String} field A field on your records
10843 * @param {String/RegExp} value Either a string that the field
10844 * should start with or a RegExp to test against the field
10845 * @param {Boolean} anyMatch True to match any part not just the beginning
10846 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10848 query : function(property, value, anyMatch){
10849 var fn = this.createFilterFn(property, value, anyMatch);
10850 return fn ? this.queryBy(fn) : this.data.clone();
10854 * Query by a function. The specified function will be called with each
10855 * record in this data source. If the function returns true the record is included
10857 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10858 * @param {Object} scope (optional) The scope of the function (defaults to this)
10859 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10861 queryBy : function(fn, scope){
10862 var data = this.snapshot || this.data;
10863 return data.filterBy(fn, scope||this);
10867 * Collects unique values for a particular dataIndex from this store.
10868 * @param {String} dataIndex The property to collect
10869 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10870 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10871 * @return {Array} An array of the unique values
10873 collect : function(dataIndex, allowNull, bypassFilter){
10874 var d = (bypassFilter === true && this.snapshot) ?
10875 this.snapshot.items : this.data.items;
10876 var v, sv, r = [], l = {};
10877 for(var i = 0, len = d.length; i < len; i++){
10878 v = d[i].data[dataIndex];
10880 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10889 * Revert to a view of the Record cache with no filtering applied.
10890 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10892 clearFilter : function(suppressEvent){
10893 if(this.snapshot && this.snapshot != this.data){
10894 this.data = this.snapshot;
10895 delete this.snapshot;
10896 if(suppressEvent !== true){
10897 this.fireEvent("datachanged", this);
10903 afterEdit : function(record){
10904 if(this.modified.indexOf(record) == -1){
10905 this.modified.push(record);
10907 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10911 afterReject : function(record){
10912 this.modified.remove(record);
10913 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10917 afterCommit : function(record){
10918 this.modified.remove(record);
10919 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10923 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10924 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10926 commitChanges : function(){
10927 var m = this.modified.slice(0);
10928 this.modified = [];
10929 for(var i = 0, len = m.length; i < len; i++){
10935 * Cancel outstanding changes on all changed records.
10937 rejectChanges : function(){
10938 var m = this.modified.slice(0);
10939 this.modified = [];
10940 for(var i = 0, len = m.length; i < len; i++){
10945 onMetaChange : function(meta, rtype, o){
10946 this.recordType = rtype;
10947 this.fields = rtype.prototype.fields;
10948 delete this.snapshot;
10949 this.sortInfo = meta.sortInfo || this.sortInfo;
10950 this.modified = [];
10951 this.fireEvent('metachange', this, this.reader.meta);
10954 moveIndex : function(data, type)
10956 var index = this.indexOf(data);
10958 var newIndex = index + type;
10962 this.insert(newIndex, data);
10967 * Ext JS Library 1.1.1
10968 * Copyright(c) 2006-2007, Ext JS, LLC.
10970 * Originally Released Under LGPL - original licence link has changed is not relivant.
10973 * <script type="text/javascript">
10977 * @class Roo.data.SimpleStore
10978 * @extends Roo.data.Store
10979 * Small helper class to make creating Stores from Array data easier.
10980 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10981 * @cfg {Array} fields An array of field definition objects, or field name strings.
10982 * @cfg {Array} data The multi-dimensional array of data
10984 * @param {Object} config
10986 Roo.data.SimpleStore = function(config){
10987 Roo.data.SimpleStore.superclass.constructor.call(this, {
10989 reader: new Roo.data.ArrayReader({
10992 Roo.data.Record.create(config.fields)
10994 proxy : new Roo.data.MemoryProxy(config.data)
10998 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11000 * Ext JS Library 1.1.1
11001 * Copyright(c) 2006-2007, Ext JS, LLC.
11003 * Originally Released Under LGPL - original licence link has changed is not relivant.
11006 * <script type="text/javascript">
11011 * @extends Roo.data.Store
11012 * @class Roo.data.JsonStore
11013 * Small helper class to make creating Stores for JSON data easier. <br/>
11015 var store = new Roo.data.JsonStore({
11016 url: 'get-images.php',
11018 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11021 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11022 * JsonReader and HttpProxy (unless inline data is provided).</b>
11023 * @cfg {Array} fields An array of field definition objects, or field name strings.
11025 * @param {Object} config
11027 Roo.data.JsonStore = function(c){
11028 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11029 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11030 reader: new Roo.data.JsonReader(c, c.fields)
11033 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11035 * Ext JS Library 1.1.1
11036 * Copyright(c) 2006-2007, Ext JS, LLC.
11038 * Originally Released Under LGPL - original licence link has changed is not relivant.
11041 * <script type="text/javascript">
11045 Roo.data.Field = function(config){
11046 if(typeof config == "string"){
11047 config = {name: config};
11049 Roo.apply(this, config);
11052 this.type = "auto";
11055 var st = Roo.data.SortTypes;
11056 // named sortTypes are supported, here we look them up
11057 if(typeof this.sortType == "string"){
11058 this.sortType = st[this.sortType];
11061 // set default sortType for strings and dates
11062 if(!this.sortType){
11065 this.sortType = st.asUCString;
11068 this.sortType = st.asDate;
11071 this.sortType = st.none;
11076 var stripRe = /[\$,%]/g;
11078 // prebuilt conversion function for this field, instead of
11079 // switching every time we're reading a value
11081 var cv, dateFormat = this.dateFormat;
11086 cv = function(v){ return v; };
11089 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11093 return v !== undefined && v !== null && v !== '' ?
11094 parseInt(String(v).replace(stripRe, ""), 10) : '';
11099 return v !== undefined && v !== null && v !== '' ?
11100 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11105 cv = function(v){ return v === true || v === "true" || v == 1; };
11112 if(v instanceof Date){
11116 if(dateFormat == "timestamp"){
11117 return new Date(v*1000);
11119 return Date.parseDate(v, dateFormat);
11121 var parsed = Date.parse(v);
11122 return parsed ? new Date(parsed) : null;
11131 Roo.data.Field.prototype = {
11139 * Ext JS Library 1.1.1
11140 * Copyright(c) 2006-2007, Ext JS, LLC.
11142 * Originally Released Under LGPL - original licence link has changed is not relivant.
11145 * <script type="text/javascript">
11148 // Base class for reading structured data from a data source. This class is intended to be
11149 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11152 * @class Roo.data.DataReader
11153 * Base class for reading structured data from a data source. This class is intended to be
11154 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11157 Roo.data.DataReader = function(meta, recordType){
11161 this.recordType = recordType instanceof Array ?
11162 Roo.data.Record.create(recordType) : recordType;
11165 Roo.data.DataReader.prototype = {
11167 * Create an empty record
11168 * @param {Object} data (optional) - overlay some values
11169 * @return {Roo.data.Record} record created.
11171 newRow : function(d) {
11173 this.recordType.prototype.fields.each(function(c) {
11175 case 'int' : da[c.name] = 0; break;
11176 case 'date' : da[c.name] = new Date(); break;
11177 case 'float' : da[c.name] = 0.0; break;
11178 case 'boolean' : da[c.name] = false; break;
11179 default : da[c.name] = ""; break;
11183 return new this.recordType(Roo.apply(da, d));
11188 * Ext JS Library 1.1.1
11189 * Copyright(c) 2006-2007, Ext JS, LLC.
11191 * Originally Released Under LGPL - original licence link has changed is not relivant.
11194 * <script type="text/javascript">
11198 * @class Roo.data.DataProxy
11199 * @extends Roo.data.Observable
11200 * This class is an abstract base class for implementations which provide retrieval of
11201 * unformatted data objects.<br>
11203 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11204 * (of the appropriate type which knows how to parse the data object) to provide a block of
11205 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11207 * Custom implementations must implement the load method as described in
11208 * {@link Roo.data.HttpProxy#load}.
11210 Roo.data.DataProxy = function(){
11213 * @event beforeload
11214 * Fires before a network request is made to retrieve a data object.
11215 * @param {Object} This DataProxy object.
11216 * @param {Object} params The params parameter to the load function.
11221 * Fires before the load method's callback is called.
11222 * @param {Object} This DataProxy object.
11223 * @param {Object} o The data object.
11224 * @param {Object} arg The callback argument object passed to the load function.
11228 * @event loadexception
11229 * Fires if an Exception occurs during data retrieval.
11230 * @param {Object} This DataProxy object.
11231 * @param {Object} o The data object.
11232 * @param {Object} arg The callback argument object passed to the load function.
11233 * @param {Object} e The Exception.
11235 loadexception : true
11237 Roo.data.DataProxy.superclass.constructor.call(this);
11240 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11243 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11247 * Ext JS Library 1.1.1
11248 * Copyright(c) 2006-2007, Ext JS, LLC.
11250 * Originally Released Under LGPL - original licence link has changed is not relivant.
11253 * <script type="text/javascript">
11256 * @class Roo.data.MemoryProxy
11257 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11258 * to the Reader when its load method is called.
11260 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11262 Roo.data.MemoryProxy = function(data){
11266 Roo.data.MemoryProxy.superclass.constructor.call(this);
11270 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11273 * Load data from the requested source (in this case an in-memory
11274 * data object passed to the constructor), read the data object into
11275 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11276 * process that block using the passed callback.
11277 * @param {Object} params This parameter is not used by the MemoryProxy class.
11278 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11279 * object into a block of Roo.data.Records.
11280 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11281 * The function must be passed <ul>
11282 * <li>The Record block object</li>
11283 * <li>The "arg" argument from the load function</li>
11284 * <li>A boolean success indicator</li>
11286 * @param {Object} scope The scope in which to call the callback
11287 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11289 load : function(params, reader, callback, scope, arg){
11290 params = params || {};
11293 result = reader.readRecords(this.data);
11295 this.fireEvent("loadexception", this, arg, null, e);
11296 callback.call(scope, null, arg, false);
11299 callback.call(scope, result, arg, true);
11303 update : function(params, records){
11308 * Ext JS Library 1.1.1
11309 * Copyright(c) 2006-2007, Ext JS, LLC.
11311 * Originally Released Under LGPL - original licence link has changed is not relivant.
11314 * <script type="text/javascript">
11317 * @class Roo.data.HttpProxy
11318 * @extends Roo.data.DataProxy
11319 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11320 * configured to reference a certain URL.<br><br>
11322 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11323 * from which the running page was served.<br><br>
11325 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11327 * Be aware that to enable the browser to parse an XML document, the server must set
11328 * the Content-Type header in the HTTP response to "text/xml".
11330 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11331 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
11332 * will be used to make the request.
11334 Roo.data.HttpProxy = function(conn){
11335 Roo.data.HttpProxy.superclass.constructor.call(this);
11336 // is conn a conn config or a real conn?
11338 this.useAjax = !conn || !conn.events;
11342 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11343 // thse are take from connection...
11346 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11349 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11350 * extra parameters to each request made by this object. (defaults to undefined)
11353 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11354 * to each request made by this object. (defaults to undefined)
11357 * @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)
11360 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11363 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11369 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11373 * Return the {@link Roo.data.Connection} object being used by this Proxy.
11374 * @return {Connection} The Connection object. This object may be used to subscribe to events on
11375 * a finer-grained basis than the DataProxy events.
11377 getConnection : function(){
11378 return this.useAjax ? Roo.Ajax : this.conn;
11382 * Load data from the configured {@link Roo.data.Connection}, read the data object into
11383 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11384 * process that block using the passed callback.
11385 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11386 * for the request to the remote server.
11387 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11388 * object into a block of Roo.data.Records.
11389 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11390 * The function must be passed <ul>
11391 * <li>The Record block object</li>
11392 * <li>The "arg" argument from the load function</li>
11393 * <li>A boolean success indicator</li>
11395 * @param {Object} scope The scope in which to call the callback
11396 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11398 load : function(params, reader, callback, scope, arg){
11399 if(this.fireEvent("beforeload", this, params) !== false){
11401 params : params || {},
11403 callback : callback,
11408 callback : this.loadResponse,
11412 Roo.applyIf(o, this.conn);
11413 if(this.activeRequest){
11414 Roo.Ajax.abort(this.activeRequest);
11416 this.activeRequest = Roo.Ajax.request(o);
11418 this.conn.request(o);
11421 callback.call(scope||this, null, arg, false);
11426 loadResponse : function(o, success, response){
11427 delete this.activeRequest;
11429 this.fireEvent("loadexception", this, o, response);
11430 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11435 result = o.reader.read(response);
11437 this.fireEvent("loadexception", this, o, response, e);
11438 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11442 this.fireEvent("load", this, o, o.request.arg);
11443 o.request.callback.call(o.request.scope, result, o.request.arg, true);
11447 update : function(dataSet){
11452 updateResponse : function(dataSet){
11457 * Ext JS Library 1.1.1
11458 * Copyright(c) 2006-2007, Ext JS, LLC.
11460 * Originally Released Under LGPL - original licence link has changed is not relivant.
11463 * <script type="text/javascript">
11467 * @class Roo.data.ScriptTagProxy
11468 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11469 * other than the originating domain of the running page.<br><br>
11471 * <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
11472 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11474 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11475 * source code that is used as the source inside a <script> tag.<br><br>
11477 * In order for the browser to process the returned data, the server must wrap the data object
11478 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11479 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11480 * depending on whether the callback name was passed:
11483 boolean scriptTag = false;
11484 String cb = request.getParameter("callback");
11487 response.setContentType("text/javascript");
11489 response.setContentType("application/x-json");
11491 Writer out = response.getWriter();
11493 out.write(cb + "(");
11495 out.print(dataBlock.toJsonString());
11502 * @param {Object} config A configuration object.
11504 Roo.data.ScriptTagProxy = function(config){
11505 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11506 Roo.apply(this, config);
11507 this.head = document.getElementsByTagName("head")[0];
11510 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11512 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11514 * @cfg {String} url The URL from which to request the data object.
11517 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11521 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11522 * the server the name of the callback function set up by the load call to process the returned data object.
11523 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11524 * javascript output which calls this named function passing the data object as its only parameter.
11526 callbackParam : "callback",
11528 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11529 * name to the request.
11534 * Load data from the configured URL, read the data object into
11535 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11536 * process that block using the passed callback.
11537 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11538 * for the request to the remote server.
11539 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11540 * object into a block of Roo.data.Records.
11541 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11542 * The function must be passed <ul>
11543 * <li>The Record block object</li>
11544 * <li>The "arg" argument from the load function</li>
11545 * <li>A boolean success indicator</li>
11547 * @param {Object} scope The scope in which to call the callback
11548 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11550 load : function(params, reader, callback, scope, arg){
11551 if(this.fireEvent("beforeload", this, params) !== false){
11553 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11555 var url = this.url;
11556 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11558 url += "&_dc=" + (new Date().getTime());
11560 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11563 cb : "stcCallback"+transId,
11564 scriptId : "stcScript"+transId,
11568 callback : callback,
11574 window[trans.cb] = function(o){
11575 conn.handleResponse(o, trans);
11578 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11580 if(this.autoAbort !== false){
11584 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11586 var script = document.createElement("script");
11587 script.setAttribute("src", url);
11588 script.setAttribute("type", "text/javascript");
11589 script.setAttribute("id", trans.scriptId);
11590 this.head.appendChild(script);
11592 this.trans = trans;
11594 callback.call(scope||this, null, arg, false);
11599 isLoading : function(){
11600 return this.trans ? true : false;
11604 * Abort the current server request.
11606 abort : function(){
11607 if(this.isLoading()){
11608 this.destroyTrans(this.trans);
11613 destroyTrans : function(trans, isLoaded){
11614 this.head.removeChild(document.getElementById(trans.scriptId));
11615 clearTimeout(trans.timeoutId);
11617 window[trans.cb] = undefined;
11619 delete window[trans.cb];
11622 // if hasn't been loaded, wait for load to remove it to prevent script error
11623 window[trans.cb] = function(){
11624 window[trans.cb] = undefined;
11626 delete window[trans.cb];
11633 handleResponse : function(o, trans){
11634 this.trans = false;
11635 this.destroyTrans(trans, true);
11638 result = trans.reader.readRecords(o);
11640 this.fireEvent("loadexception", this, o, trans.arg, e);
11641 trans.callback.call(trans.scope||window, null, trans.arg, false);
11644 this.fireEvent("load", this, o, trans.arg);
11645 trans.callback.call(trans.scope||window, result, trans.arg, true);
11649 handleFailure : function(trans){
11650 this.trans = false;
11651 this.destroyTrans(trans, false);
11652 this.fireEvent("loadexception", this, null, trans.arg);
11653 trans.callback.call(trans.scope||window, null, trans.arg, false);
11657 * Ext JS Library 1.1.1
11658 * Copyright(c) 2006-2007, Ext JS, LLC.
11660 * Originally Released Under LGPL - original licence link has changed is not relivant.
11663 * <script type="text/javascript">
11667 * @class Roo.data.JsonReader
11668 * @extends Roo.data.DataReader
11669 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11670 * based on mappings in a provided Roo.data.Record constructor.
11672 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11673 * in the reply previously.
11678 var RecordDef = Roo.data.Record.create([
11679 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
11680 {name: 'occupation'} // This field will use "occupation" as the mapping.
11682 var myReader = new Roo.data.JsonReader({
11683 totalProperty: "results", // The property which contains the total dataset size (optional)
11684 root: "rows", // The property which contains an Array of row objects
11685 id: "id" // The property within each row object that provides an ID for the record (optional)
11689 * This would consume a JSON file like this:
11691 { 'results': 2, 'rows': [
11692 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11693 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11696 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11697 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11698 * paged from the remote server.
11699 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11700 * @cfg {String} root name of the property which contains the Array of row objects.
11701 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11702 * @cfg {Array} fields Array of field definition objects
11704 * Create a new JsonReader
11705 * @param {Object} meta Metadata configuration options
11706 * @param {Object} recordType Either an Array of field definition objects,
11707 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11709 Roo.data.JsonReader = function(meta, recordType){
11712 // set some defaults:
11713 Roo.applyIf(meta, {
11714 totalProperty: 'total',
11715 successProperty : 'success',
11720 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11722 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11725 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11726 * Used by Store query builder to append _requestMeta to params.
11729 metaFromRemote : false,
11731 * This method is only used by a DataProxy which has retrieved data from a remote server.
11732 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11733 * @return {Object} data A data block which is used by an Roo.data.Store object as
11734 * a cache of Roo.data.Records.
11736 read : function(response){
11737 var json = response.responseText;
11739 var o = /* eval:var:o */ eval("("+json+")");
11741 throw {message: "JsonReader.read: Json object not found"};
11747 this.metaFromRemote = true;
11748 this.meta = o.metaData;
11749 this.recordType = Roo.data.Record.create(o.metaData.fields);
11750 this.onMetaChange(this.meta, this.recordType, o);
11752 return this.readRecords(o);
11755 // private function a store will implement
11756 onMetaChange : function(meta, recordType, o){
11763 simpleAccess: function(obj, subsc) {
11770 getJsonAccessor: function(){
11772 return function(expr) {
11774 return(re.test(expr))
11775 ? new Function("obj", "return obj." + expr)
11780 return Roo.emptyFn;
11785 * Create a data block containing Roo.data.Records from an XML document.
11786 * @param {Object} o An object which contains an Array of row objects in the property specified
11787 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11788 * which contains the total size of the dataset.
11789 * @return {Object} data A data block which is used by an Roo.data.Store object as
11790 * a cache of Roo.data.Records.
11792 readRecords : function(o){
11794 * After any data loads, the raw JSON data is available for further custom processing.
11798 var s = this.meta, Record = this.recordType,
11799 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11801 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11803 if(s.totalProperty) {
11804 this.getTotal = this.getJsonAccessor(s.totalProperty);
11806 if(s.successProperty) {
11807 this.getSuccess = this.getJsonAccessor(s.successProperty);
11809 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11811 var g = this.getJsonAccessor(s.id);
11812 this.getId = function(rec) {
11814 return (r === undefined || r === "") ? null : r;
11817 this.getId = function(){return null;};
11820 for(var jj = 0; jj < fl; jj++){
11822 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11823 this.ef[jj] = this.getJsonAccessor(map);
11827 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11828 if(s.totalProperty){
11829 var vt = parseInt(this.getTotal(o), 10);
11834 if(s.successProperty){
11835 var vs = this.getSuccess(o);
11836 if(vs === false || vs === 'false'){
11841 for(var i = 0; i < c; i++){
11844 var id = this.getId(n);
11845 for(var j = 0; j < fl; j++){
11847 var v = this.ef[j](n);
11849 Roo.log('missing convert for ' + f.name);
11853 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11855 var record = new Record(values, id);
11857 records[i] = record;
11863 totalRecords : totalRecords
11868 * Ext JS Library 1.1.1
11869 * Copyright(c) 2006-2007, Ext JS, LLC.
11871 * Originally Released Under LGPL - original licence link has changed is not relivant.
11874 * <script type="text/javascript">
11878 * @class Roo.data.ArrayReader
11879 * @extends Roo.data.DataReader
11880 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11881 * Each element of that Array represents a row of data fields. The
11882 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11883 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11887 var RecordDef = Roo.data.Record.create([
11888 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11889 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11891 var myReader = new Roo.data.ArrayReader({
11892 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11896 * This would consume an Array like this:
11898 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11900 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11902 * Create a new JsonReader
11903 * @param {Object} meta Metadata configuration options.
11904 * @param {Object} recordType Either an Array of field definition objects
11905 * as specified to {@link Roo.data.Record#create},
11906 * or an {@link Roo.data.Record} object
11907 * created using {@link Roo.data.Record#create}.
11909 Roo.data.ArrayReader = function(meta, recordType){
11910 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11913 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11915 * Create a data block containing Roo.data.Records from an XML document.
11916 * @param {Object} o An Array of row objects which represents the dataset.
11917 * @return {Object} data A data block which is used by an Roo.data.Store object as
11918 * a cache of Roo.data.Records.
11920 readRecords : function(o){
11921 var sid = this.meta ? this.meta.id : null;
11922 var recordType = this.recordType, fields = recordType.prototype.fields;
11925 for(var i = 0; i < root.length; i++){
11928 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11929 for(var j = 0, jlen = fields.length; j < jlen; j++){
11930 var f = fields.items[j];
11931 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11932 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11934 values[f.name] = v;
11936 var record = new recordType(values, id);
11938 records[records.length] = record;
11942 totalRecords : records.length
11951 * @class Roo.bootstrap.ComboBox
11952 * @extends Roo.bootstrap.TriggerField
11953 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11954 * @cfg {Boolean} append (true|false) default false
11955 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11956 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11957 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11958 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11959 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11960 * @cfg {Boolean} animate default true
11961 * @cfg {Boolean} emptyResultText only for touch device
11962 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11964 * Create a new ComboBox.
11965 * @param {Object} config Configuration options
11967 Roo.bootstrap.ComboBox = function(config){
11968 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11972 * Fires when the dropdown list is expanded
11973 * @param {Roo.bootstrap.ComboBox} combo This combo box
11978 * Fires when the dropdown list is collapsed
11979 * @param {Roo.bootstrap.ComboBox} combo This combo box
11983 * @event beforeselect
11984 * Fires before a list item is selected. Return false to cancel the selection.
11985 * @param {Roo.bootstrap.ComboBox} combo This combo box
11986 * @param {Roo.data.Record} record The data record returned from the underlying store
11987 * @param {Number} index The index of the selected item in the dropdown list
11989 'beforeselect' : true,
11992 * Fires when a list item is selected
11993 * @param {Roo.bootstrap.ComboBox} combo This combo box
11994 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11995 * @param {Number} index The index of the selected item in the dropdown list
11999 * @event beforequery
12000 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12001 * The event object passed has these properties:
12002 * @param {Roo.bootstrap.ComboBox} combo This combo box
12003 * @param {String} query The query
12004 * @param {Boolean} forceAll true to force "all" query
12005 * @param {Boolean} cancel true to cancel the query
12006 * @param {Object} e The query event object
12008 'beforequery': true,
12011 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12012 * @param {Roo.bootstrap.ComboBox} combo This combo box
12017 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12018 * @param {Roo.bootstrap.ComboBox} combo This combo box
12019 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12024 * Fires when the remove value from the combobox array
12025 * @param {Roo.bootstrap.ComboBox} combo This combo box
12029 * @event afterremove
12030 * Fires when the remove value from the combobox array
12031 * @param {Roo.bootstrap.ComboBox} combo This combo box
12033 'afterremove' : true,
12035 * @event specialfilter
12036 * Fires when specialfilter
12037 * @param {Roo.bootstrap.ComboBox} combo This combo box
12039 'specialfilter' : true,
12042 * Fires when tick the element
12043 * @param {Roo.bootstrap.ComboBox} combo This combo box
12047 * @event touchviewdisplay
12048 * Fires when touch view require special display (default is using displayField)
12049 * @param {Roo.bootstrap.ComboBox} combo This combo box
12050 * @param {Object} cfg set html .
12052 'touchviewdisplay' : true
12057 this.tickItems = [];
12059 this.selectedIndex = -1;
12060 if(this.mode == 'local'){
12061 if(config.queryDelay === undefined){
12062 this.queryDelay = 10;
12064 if(config.minChars === undefined){
12070 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12073 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12074 * rendering into an Roo.Editor, defaults to false)
12077 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12078 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12081 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12084 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12085 * the dropdown list (defaults to undefined, with no header element)
12089 * @cfg {String/Roo.Template} tpl The template to use to render the output
12093 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12095 listWidth: undefined,
12097 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12098 * mode = 'remote' or 'text' if mode = 'local')
12100 displayField: undefined,
12103 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12104 * mode = 'remote' or 'value' if mode = 'local').
12105 * Note: use of a valueField requires the user make a selection
12106 * in order for a value to be mapped.
12108 valueField: undefined,
12110 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12115 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12116 * field's data value (defaults to the underlying DOM element's name)
12118 hiddenName: undefined,
12120 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12124 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12126 selectedClass: 'active',
12129 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12133 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12134 * anchor positions (defaults to 'tl-bl')
12136 listAlign: 'tl-bl?',
12138 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12142 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12143 * query specified by the allQuery config option (defaults to 'query')
12145 triggerAction: 'query',
12147 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12148 * (defaults to 4, does not apply if editable = false)
12152 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12153 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12157 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12158 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12162 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12163 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12167 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12168 * when editable = true (defaults to false)
12170 selectOnFocus:false,
12172 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12174 queryParam: 'query',
12176 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12177 * when mode = 'remote' (defaults to 'Loading...')
12179 loadingText: 'Loading...',
12181 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12185 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12189 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12190 * traditional select (defaults to true)
12194 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12198 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12202 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12203 * listWidth has a higher value)
12207 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12208 * allow the user to set arbitrary text into the field (defaults to false)
12210 forceSelection:false,
12212 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12213 * if typeAhead = true (defaults to 250)
12215 typeAheadDelay : 250,
12217 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12218 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12220 valueNotFoundText : undefined,
12222 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12224 blockFocus : false,
12227 * @cfg {Boolean} disableClear Disable showing of clear button.
12229 disableClear : false,
12231 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12233 alwaysQuery : false,
12236 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12241 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12243 invalidClass : "has-warning",
12246 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12248 validClass : "has-success",
12251 * @cfg {Boolean} specialFilter (true|false) special filter default false
12253 specialFilter : false,
12256 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12258 mobileTouchView : true,
12261 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12263 useNativeIOS : false,
12265 ios_options : false,
12277 btnPosition : 'right',
12278 triggerList : true,
12279 showToggleBtn : true,
12281 emptyResultText: 'Empty',
12282 triggerText : 'Select',
12284 // element that contains real text value.. (when hidden is used..)
12286 getAutoCreate : function()
12291 * Render classic select for iso
12294 if(Roo.isIOS && this.useNativeIOS){
12295 cfg = this.getAutoCreateNativeIOS();
12303 if(Roo.isTouch && this.mobileTouchView){
12304 cfg = this.getAutoCreateTouchView();
12311 if(!this.tickable){
12312 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12317 * ComboBox with tickable selections
12320 var align = this.labelAlign || this.parentLabelAlign();
12323 cls : 'form-group roo-combobox-tickable' //input-group
12328 cls : 'tickable-buttons',
12333 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12334 html : this.triggerText
12340 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12347 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12354 buttons.cn.unshift({
12356 cls: 'roo-select2-search-field-input'
12362 Roo.each(buttons.cn, function(c){
12364 c.cls += ' btn-' + _this.size;
12367 if (_this.disabled) {
12378 cls: 'form-hidden-field'
12382 cls: 'roo-select2-choices',
12386 cls: 'roo-select2-search-field',
12398 cls: 'roo-select2-container input-group roo-select2-container-multi',
12403 // cls: 'typeahead typeahead-long dropdown-menu',
12404 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
12409 if(this.hasFeedback && !this.allowBlank){
12413 cls: 'glyphicon form-control-feedback'
12416 combobox.cn.push(feedback);
12419 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12421 // Roo.log("left and has label");
12425 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12426 tooltip : 'This field is required'
12431 cls : 'control-label col-sm-' + this.labelWidth,
12432 html : this.fieldLabel
12436 cls : "col-sm-" + (12 - this.labelWidth),
12444 if(this.indicatorpos == 'right'){
12450 cls : 'control-label col-sm-' + this.labelWidth,
12451 html : this.fieldLabel
12456 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12457 tooltip : 'This field is required'
12460 cls : "col-sm-" + (12 - this.labelWidth),
12471 } else if ( this.fieldLabel.length) {
12472 // Roo.log(" label");
12476 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12477 tooltip : 'This field is required'
12481 //cls : 'input-group-addon',
12482 html : this.fieldLabel
12490 if(this.indicatorpos == 'right'){
12495 //cls : 'input-group-addon',
12496 html : this.fieldLabel
12502 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12503 tooltip : 'This field is required'
12514 // Roo.log(" no label && no align");
12521 ['xs','sm','md','lg'].map(function(size){
12522 if (settings[size]) {
12523 cfg.cls += ' col-' + size + '-' + settings[size];
12531 _initEventsCalled : false,
12534 initEvents: function()
12536 if (this._initEventsCalled) { // as we call render... prevent looping...
12539 this._initEventsCalled = true;
12542 throw "can not find store for combo";
12545 this.store = Roo.factory(this.store, Roo.data);
12547 // if we are building from html. then this element is so complex, that we can not really
12548 // use the rendered HTML.
12549 // so we have to trash and replace the previous code.
12550 if (Roo.XComponent.build_from_html) {
12552 // remove this element....
12553 var e = this.el.dom, k=0;
12554 while (e ) { e = e.previousSibling; ++k;}
12559 this.rendered = false;
12561 this.render(this.parent().getChildContainer(true), k);
12567 if(Roo.isIOS && this.useNativeIOS){
12568 this.initIOSView();
12576 if(Roo.isTouch && this.mobileTouchView){
12577 this.initTouchView();
12582 this.initTickableEvents();
12586 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12588 if(this.hiddenName){
12590 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12592 this.hiddenField.dom.value =
12593 this.hiddenValue !== undefined ? this.hiddenValue :
12594 this.value !== undefined ? this.value : '';
12596 // prevent input submission
12597 this.el.dom.removeAttribute('name');
12598 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12603 // this.el.dom.setAttribute('autocomplete', 'off');
12606 var cls = 'x-combo-list';
12608 //this.list = new Roo.Layer({
12609 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12615 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12616 _this.list.setWidth(lw);
12619 this.list.on('mouseover', this.onViewOver, this);
12620 this.list.on('mousemove', this.onViewMove, this);
12622 this.list.on('scroll', this.onViewScroll, this);
12625 this.list.swallowEvent('mousewheel');
12626 this.assetHeight = 0;
12629 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12630 this.assetHeight += this.header.getHeight();
12633 this.innerList = this.list.createChild({cls:cls+'-inner'});
12634 this.innerList.on('mouseover', this.onViewOver, this);
12635 this.innerList.on('mousemove', this.onViewMove, this);
12636 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12638 if(this.allowBlank && !this.pageSize && !this.disableClear){
12639 this.footer = this.list.createChild({cls:cls+'-ft'});
12640 this.pageTb = new Roo.Toolbar(this.footer);
12644 this.footer = this.list.createChild({cls:cls+'-ft'});
12645 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12646 {pageSize: this.pageSize});
12650 if (this.pageTb && this.allowBlank && !this.disableClear) {
12652 this.pageTb.add(new Roo.Toolbar.Fill(), {
12653 cls: 'x-btn-icon x-btn-clear',
12655 handler: function()
12658 _this.clearValue();
12659 _this.onSelect(false, -1);
12664 this.assetHeight += this.footer.getHeight();
12669 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12672 this.view = new Roo.View(this.list, this.tpl, {
12673 singleSelect:true, store: this.store, selectedClass: this.selectedClass
12675 //this.view.wrapEl.setDisplayed(false);
12676 this.view.on('click', this.onViewClick, this);
12680 this.store.on('beforeload', this.onBeforeLoad, this);
12681 this.store.on('load', this.onLoad, this);
12682 this.store.on('loadexception', this.onLoadException, this);
12684 if(this.resizable){
12685 this.resizer = new Roo.Resizable(this.list, {
12686 pinned:true, handles:'se'
12688 this.resizer.on('resize', function(r, w, h){
12689 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12690 this.listWidth = w;
12691 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12692 this.restrictHeight();
12694 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12697 if(!this.editable){
12698 this.editable = true;
12699 this.setEditable(false);
12704 if (typeof(this.events.add.listeners) != 'undefined') {
12706 this.addicon = this.wrap.createChild(
12707 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
12709 this.addicon.on('click', function(e) {
12710 this.fireEvent('add', this);
12713 if (typeof(this.events.edit.listeners) != 'undefined') {
12715 this.editicon = this.wrap.createChild(
12716 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
12717 if (this.addicon) {
12718 this.editicon.setStyle('margin-left', '40px');
12720 this.editicon.on('click', function(e) {
12722 // we fire even if inothing is selected..
12723 this.fireEvent('edit', this, this.lastData );
12729 this.keyNav = new Roo.KeyNav(this.inputEl(), {
12730 "up" : function(e){
12731 this.inKeyMode = true;
12735 "down" : function(e){
12736 if(!this.isExpanded()){
12737 this.onTriggerClick();
12739 this.inKeyMode = true;
12744 "enter" : function(e){
12745 // this.onViewClick();
12749 if(this.fireEvent("specialkey", this, e)){
12750 this.onViewClick(false);
12756 "esc" : function(e){
12760 "tab" : function(e){
12763 if(this.fireEvent("specialkey", this, e)){
12764 this.onViewClick(false);
12772 doRelay : function(foo, bar, hname){
12773 if(hname == 'down' || this.scope.isExpanded()){
12774 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12783 this.queryDelay = Math.max(this.queryDelay || 10,
12784 this.mode == 'local' ? 10 : 250);
12787 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12789 if(this.typeAhead){
12790 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12792 if(this.editable !== false){
12793 this.inputEl().on("keyup", this.onKeyUp, this);
12795 if(this.forceSelection){
12796 this.inputEl().on('blur', this.doForce, this);
12800 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12801 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12805 initTickableEvents: function()
12809 if(this.hiddenName){
12811 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12813 this.hiddenField.dom.value =
12814 this.hiddenValue !== undefined ? this.hiddenValue :
12815 this.value !== undefined ? this.value : '';
12817 // prevent input submission
12818 this.el.dom.removeAttribute('name');
12819 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12824 // this.list = this.el.select('ul.dropdown-menu',true).first();
12826 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12827 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12828 if(this.triggerList){
12829 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12832 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12833 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12835 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12836 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12838 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12839 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12841 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12842 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12843 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12846 this.cancelBtn.hide();
12851 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12852 _this.list.setWidth(lw);
12855 this.list.on('mouseover', this.onViewOver, this);
12856 this.list.on('mousemove', this.onViewMove, this);
12858 this.list.on('scroll', this.onViewScroll, this);
12861 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>';
12864 this.view = new Roo.View(this.list, this.tpl, {
12865 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12868 //this.view.wrapEl.setDisplayed(false);
12869 this.view.on('click', this.onViewClick, this);
12873 this.store.on('beforeload', this.onBeforeLoad, this);
12874 this.store.on('load', this.onLoad, this);
12875 this.store.on('loadexception', this.onLoadException, this);
12878 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12879 "up" : function(e){
12880 this.inKeyMode = true;
12884 "down" : function(e){
12885 this.inKeyMode = true;
12889 "enter" : function(e){
12890 if(this.fireEvent("specialkey", this, e)){
12891 this.onViewClick(false);
12897 "esc" : function(e){
12898 this.onTickableFooterButtonClick(e, false, false);
12901 "tab" : function(e){
12902 this.fireEvent("specialkey", this, e);
12904 this.onTickableFooterButtonClick(e, false, false);
12911 doRelay : function(e, fn, key){
12912 if(this.scope.isExpanded()){
12913 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12922 this.queryDelay = Math.max(this.queryDelay || 10,
12923 this.mode == 'local' ? 10 : 250);
12926 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12928 if(this.typeAhead){
12929 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12932 if(this.editable !== false){
12933 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12938 onDestroy : function(){
12940 this.view.setStore(null);
12941 this.view.el.removeAllListeners();
12942 this.view.el.remove();
12943 this.view.purgeListeners();
12946 this.list.dom.innerHTML = '';
12950 this.store.un('beforeload', this.onBeforeLoad, this);
12951 this.store.un('load', this.onLoad, this);
12952 this.store.un('loadexception', this.onLoadException, this);
12954 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12958 fireKey : function(e){
12959 if(e.isNavKeyPress() && !this.list.isVisible()){
12960 this.fireEvent("specialkey", this, e);
12965 onResize: function(w, h){
12966 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12968 // if(typeof w != 'number'){
12969 // // we do not handle it!?!?
12972 // var tw = this.trigger.getWidth();
12973 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12974 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12976 // this.inputEl().setWidth( this.adjustWidth('input', x));
12978 // //this.trigger.setStyle('left', x+'px');
12980 // if(this.list && this.listWidth === undefined){
12981 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12982 // this.list.setWidth(lw);
12983 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12991 * Allow or prevent the user from directly editing the field text. If false is passed,
12992 * the user will only be able to select from the items defined in the dropdown list. This method
12993 * is the runtime equivalent of setting the 'editable' config option at config time.
12994 * @param {Boolean} value True to allow the user to directly edit the field text
12996 setEditable : function(value){
12997 if(value == this.editable){
13000 this.editable = value;
13002 this.inputEl().dom.setAttribute('readOnly', true);
13003 this.inputEl().on('mousedown', this.onTriggerClick, this);
13004 this.inputEl().addClass('x-combo-noedit');
13006 this.inputEl().dom.setAttribute('readOnly', false);
13007 this.inputEl().un('mousedown', this.onTriggerClick, this);
13008 this.inputEl().removeClass('x-combo-noedit');
13014 onBeforeLoad : function(combo,opts){
13015 if(!this.hasFocus){
13019 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13021 this.restrictHeight();
13022 this.selectedIndex = -1;
13026 onLoad : function(){
13028 this.hasQuery = false;
13030 if(!this.hasFocus){
13034 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13035 this.loading.hide();
13038 if(this.store.getCount() > 0){
13040 this.restrictHeight();
13041 if(this.lastQuery == this.allQuery){
13042 if(this.editable && !this.tickable){
13043 this.inputEl().dom.select();
13047 !this.selectByValue(this.value, true) &&
13050 !this.store.lastOptions ||
13051 typeof(this.store.lastOptions.add) == 'undefined' ||
13052 this.store.lastOptions.add != true
13055 this.select(0, true);
13058 if(this.autoFocus){
13061 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13062 this.taTask.delay(this.typeAheadDelay);
13066 this.onEmptyResults();
13072 onLoadException : function()
13074 this.hasQuery = false;
13076 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13077 this.loading.hide();
13080 if(this.tickable && this.editable){
13085 // only causes errors at present
13086 //Roo.log(this.store.reader.jsonData);
13087 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13089 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13095 onTypeAhead : function(){
13096 if(this.store.getCount() > 0){
13097 var r = this.store.getAt(0);
13098 var newValue = r.data[this.displayField];
13099 var len = newValue.length;
13100 var selStart = this.getRawValue().length;
13102 if(selStart != len){
13103 this.setRawValue(newValue);
13104 this.selectText(selStart, newValue.length);
13110 onSelect : function(record, index){
13112 if(this.fireEvent('beforeselect', this, record, index) !== false){
13114 this.setFromData(index > -1 ? record.data : false);
13117 this.fireEvent('select', this, record, index);
13122 * Returns the currently selected field value or empty string if no value is set.
13123 * @return {String} value The selected value
13125 getValue : function()
13127 if(Roo.isIOS && this.useNativeIOS){
13128 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13132 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13135 if(this.valueField){
13136 return typeof this.value != 'undefined' ? this.value : '';
13138 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13142 getRawValue : function()
13144 if(Roo.isIOS && this.useNativeIOS){
13145 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13148 var v = this.inputEl().getValue();
13154 * Clears any text/value currently set in the field
13156 clearValue : function(){
13158 if(this.hiddenField){
13159 this.hiddenField.dom.value = '';
13162 this.setRawValue('');
13163 this.lastSelectionText = '';
13164 this.lastData = false;
13166 var close = this.closeTriggerEl();
13177 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13178 * will be displayed in the field. If the value does not match the data value of an existing item,
13179 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13180 * Otherwise the field will be blank (although the value will still be set).
13181 * @param {String} value The value to match
13183 setValue : function(v)
13185 if(Roo.isIOS && this.useNativeIOS){
13186 this.setIOSValue(v);
13196 if(this.valueField){
13197 var r = this.findRecord(this.valueField, v);
13199 text = r.data[this.displayField];
13200 }else if(this.valueNotFoundText !== undefined){
13201 text = this.valueNotFoundText;
13204 this.lastSelectionText = text;
13205 if(this.hiddenField){
13206 this.hiddenField.dom.value = v;
13208 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13211 var close = this.closeTriggerEl();
13214 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13220 * @property {Object} the last set data for the element
13225 * Sets the value of the field based on a object which is related to the record format for the store.
13226 * @param {Object} value the value to set as. or false on reset?
13228 setFromData : function(o){
13235 var dv = ''; // display value
13236 var vv = ''; // value value..
13238 if (this.displayField) {
13239 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13241 // this is an error condition!!!
13242 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13245 if(this.valueField){
13246 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13249 var close = this.closeTriggerEl();
13252 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13255 if(this.hiddenField){
13256 this.hiddenField.dom.value = vv;
13258 this.lastSelectionText = dv;
13259 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13263 // no hidden field.. - we store the value in 'value', but still display
13264 // display field!!!!
13265 this.lastSelectionText = dv;
13266 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13273 reset : function(){
13274 // overridden so that last data is reset..
13281 this.setValue(this.originalValue);
13282 //this.clearInvalid();
13283 this.lastData = false;
13285 this.view.clearSelections();
13291 findRecord : function(prop, value){
13293 if(this.store.getCount() > 0){
13294 this.store.each(function(r){
13295 if(r.data[prop] == value){
13305 getName: function()
13307 // returns hidden if it's set..
13308 if (!this.rendered) {return ''};
13309 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
13313 onViewMove : function(e, t){
13314 this.inKeyMode = false;
13318 onViewOver : function(e, t){
13319 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13322 var item = this.view.findItemFromChild(t);
13325 var index = this.view.indexOf(item);
13326 this.select(index, false);
13331 onViewClick : function(view, doFocus, el, e)
13333 var index = this.view.getSelectedIndexes()[0];
13335 var r = this.store.getAt(index);
13339 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13346 Roo.each(this.tickItems, function(v,k){
13348 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13350 _this.tickItems.splice(k, 1);
13352 if(typeof(e) == 'undefined' && view == false){
13353 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13365 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13366 this.tickItems.push(r.data);
13369 if(typeof(e) == 'undefined' && view == false){
13370 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13377 this.onSelect(r, index);
13379 if(doFocus !== false && !this.blockFocus){
13380 this.inputEl().focus();
13385 restrictHeight : function(){
13386 //this.innerList.dom.style.height = '';
13387 //var inner = this.innerList.dom;
13388 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13389 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13390 //this.list.beginUpdate();
13391 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13392 this.list.alignTo(this.inputEl(), this.listAlign);
13393 this.list.alignTo(this.inputEl(), this.listAlign);
13394 //this.list.endUpdate();
13398 onEmptyResults : function(){
13400 if(this.tickable && this.editable){
13401 this.restrictHeight();
13409 * Returns true if the dropdown list is expanded, else false.
13411 isExpanded : function(){
13412 return this.list.isVisible();
13416 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13417 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13418 * @param {String} value The data value of the item to select
13419 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13420 * selected item if it is not currently in view (defaults to true)
13421 * @return {Boolean} True if the value matched an item in the list, else false
13423 selectByValue : function(v, scrollIntoView){
13424 if(v !== undefined && v !== null){
13425 var r = this.findRecord(this.valueField || this.displayField, v);
13427 this.select(this.store.indexOf(r), scrollIntoView);
13435 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13436 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13437 * @param {Number} index The zero-based index of the list item to select
13438 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13439 * selected item if it is not currently in view (defaults to true)
13441 select : function(index, scrollIntoView){
13442 this.selectedIndex = index;
13443 this.view.select(index);
13444 if(scrollIntoView !== false){
13445 var el = this.view.getNode(index);
13447 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13450 this.list.scrollChildIntoView(el, false);
13456 selectNext : function(){
13457 var ct = this.store.getCount();
13459 if(this.selectedIndex == -1){
13461 }else if(this.selectedIndex < ct-1){
13462 this.select(this.selectedIndex+1);
13468 selectPrev : function(){
13469 var ct = this.store.getCount();
13471 if(this.selectedIndex == -1){
13473 }else if(this.selectedIndex != 0){
13474 this.select(this.selectedIndex-1);
13480 onKeyUp : function(e){
13481 if(this.editable !== false && !e.isSpecialKey()){
13482 this.lastKey = e.getKey();
13483 this.dqTask.delay(this.queryDelay);
13488 validateBlur : function(){
13489 return !this.list || !this.list.isVisible();
13493 initQuery : function(){
13495 var v = this.getRawValue();
13497 if(this.tickable && this.editable){
13498 v = this.tickableInputEl().getValue();
13505 doForce : function(){
13506 if(this.inputEl().dom.value.length > 0){
13507 this.inputEl().dom.value =
13508 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13514 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
13515 * query allowing the query action to be canceled if needed.
13516 * @param {String} query The SQL query to execute
13517 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13518 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
13519 * saved in the current store (defaults to false)
13521 doQuery : function(q, forceAll){
13523 if(q === undefined || q === null){
13528 forceAll: forceAll,
13532 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13537 forceAll = qe.forceAll;
13538 if(forceAll === true || (q.length >= this.minChars)){
13540 this.hasQuery = true;
13542 if(this.lastQuery != q || this.alwaysQuery){
13543 this.lastQuery = q;
13544 if(this.mode == 'local'){
13545 this.selectedIndex = -1;
13547 this.store.clearFilter();
13550 if(this.specialFilter){
13551 this.fireEvent('specialfilter', this);
13556 this.store.filter(this.displayField, q);
13559 this.store.fireEvent("datachanged", this.store);
13566 this.store.baseParams[this.queryParam] = q;
13568 var options = {params : this.getParams(q)};
13571 options.add = true;
13572 options.params.start = this.page * this.pageSize;
13575 this.store.load(options);
13578 * this code will make the page width larger, at the beginning, the list not align correctly,
13579 * we should expand the list on onLoad
13580 * so command out it
13585 this.selectedIndex = -1;
13590 this.loadNext = false;
13594 getParams : function(q){
13596 //p[this.queryParam] = q;
13600 p.limit = this.pageSize;
13606 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13608 collapse : function(){
13609 if(!this.isExpanded()){
13616 this.hasFocus = false;
13618 this.cancelBtn.hide();
13619 this.trigger.show();
13622 this.tickableInputEl().dom.value = '';
13623 this.tickableInputEl().blur();
13628 Roo.get(document).un('mousedown', this.collapseIf, this);
13629 Roo.get(document).un('mousewheel', this.collapseIf, this);
13630 if (!this.editable) {
13631 Roo.get(document).un('keydown', this.listKeyPress, this);
13633 this.fireEvent('collapse', this);
13639 collapseIf : function(e){
13640 var in_combo = e.within(this.el);
13641 var in_list = e.within(this.list);
13642 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13644 if (in_combo || in_list || is_list) {
13645 //e.stopPropagation();
13650 this.onTickableFooterButtonClick(e, false, false);
13658 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13660 expand : function(){
13662 if(this.isExpanded() || !this.hasFocus){
13666 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13667 this.list.setWidth(lw);
13674 this.restrictHeight();
13678 this.tickItems = Roo.apply([], this.item);
13681 this.cancelBtn.show();
13682 this.trigger.hide();
13685 this.tickableInputEl().focus();
13690 Roo.get(document).on('mousedown', this.collapseIf, this);
13691 Roo.get(document).on('mousewheel', this.collapseIf, this);
13692 if (!this.editable) {
13693 Roo.get(document).on('keydown', this.listKeyPress, this);
13696 this.fireEvent('expand', this);
13700 // Implements the default empty TriggerField.onTriggerClick function
13701 onTriggerClick : function(e)
13703 Roo.log('trigger click');
13705 if(this.disabled || !this.triggerList){
13710 this.loadNext = false;
13712 if(this.isExpanded()){
13714 if (!this.blockFocus) {
13715 this.inputEl().focus();
13719 this.hasFocus = true;
13720 if(this.triggerAction == 'all') {
13721 this.doQuery(this.allQuery, true);
13723 this.doQuery(this.getRawValue());
13725 if (!this.blockFocus) {
13726 this.inputEl().focus();
13731 onTickableTriggerClick : function(e)
13738 this.loadNext = false;
13739 this.hasFocus = true;
13741 if(this.triggerAction == 'all') {
13742 this.doQuery(this.allQuery, true);
13744 this.doQuery(this.getRawValue());
13748 onSearchFieldClick : function(e)
13750 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13751 this.onTickableFooterButtonClick(e, false, false);
13755 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13760 this.loadNext = false;
13761 this.hasFocus = true;
13763 if(this.triggerAction == 'all') {
13764 this.doQuery(this.allQuery, true);
13766 this.doQuery(this.getRawValue());
13770 listKeyPress : function(e)
13772 //Roo.log('listkeypress');
13773 // scroll to first matching element based on key pres..
13774 if (e.isSpecialKey()) {
13777 var k = String.fromCharCode(e.getKey()).toUpperCase();
13780 var csel = this.view.getSelectedNodes();
13781 var cselitem = false;
13783 var ix = this.view.indexOf(csel[0]);
13784 cselitem = this.store.getAt(ix);
13785 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13791 this.store.each(function(v) {
13793 // start at existing selection.
13794 if (cselitem.id == v.id) {
13800 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13801 match = this.store.indexOf(v);
13807 if (match === false) {
13808 return true; // no more action?
13811 this.view.select(match);
13812 var sn = Roo.get(this.view.getSelectedNodes()[0]);
13813 sn.scrollIntoView(sn.dom.parentNode, false);
13816 onViewScroll : function(e, t){
13818 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){
13822 this.hasQuery = true;
13824 this.loading = this.list.select('.loading', true).first();
13826 if(this.loading === null){
13827 this.list.createChild({
13829 cls: 'loading roo-select2-more-results roo-select2-active',
13830 html: 'Loading more results...'
13833 this.loading = this.list.select('.loading', true).first();
13835 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13837 this.loading.hide();
13840 this.loading.show();
13845 this.loadNext = true;
13847 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13852 addItem : function(o)
13854 var dv = ''; // display value
13856 if (this.displayField) {
13857 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13859 // this is an error condition!!!
13860 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13867 var choice = this.choices.createChild({
13869 cls: 'roo-select2-search-choice',
13878 cls: 'roo-select2-search-choice-close',
13883 }, this.searchField);
13885 var close = choice.select('a.roo-select2-search-choice-close', true).first();
13887 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13895 this.inputEl().dom.value = '';
13900 onRemoveItem : function(e, _self, o)
13902 e.preventDefault();
13904 this.lastItem = Roo.apply([], this.item);
13906 var index = this.item.indexOf(o.data) * 1;
13909 Roo.log('not this item?!');
13913 this.item.splice(index, 1);
13918 this.fireEvent('remove', this, e);
13924 syncValue : function()
13926 if(!this.item.length){
13933 Roo.each(this.item, function(i){
13934 if(_this.valueField){
13935 value.push(i[_this.valueField]);
13942 this.value = value.join(',');
13944 if(this.hiddenField){
13945 this.hiddenField.dom.value = this.value;
13948 this.store.fireEvent("datachanged", this.store);
13953 clearItem : function()
13955 if(!this.multiple){
13961 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13969 if(this.tickable && !Roo.isTouch){
13970 this.view.refresh();
13974 inputEl: function ()
13976 if(Roo.isIOS && this.useNativeIOS){
13977 return this.el.select('select.roo-ios-select', true).first();
13980 if(Roo.isTouch && this.mobileTouchView){
13981 return this.el.select('input.form-control',true).first();
13985 return this.searchField;
13988 return this.el.select('input.form-control',true).first();
13991 onTickableFooterButtonClick : function(e, btn, el)
13993 e.preventDefault();
13995 this.lastItem = Roo.apply([], this.item);
13997 if(btn && btn.name == 'cancel'){
13998 this.tickItems = Roo.apply([], this.item);
14007 Roo.each(this.tickItems, function(o){
14015 validate : function()
14017 var v = this.getRawValue();
14020 v = this.getValue();
14023 if(this.disabled || this.allowBlank || v.length){
14028 this.markInvalid();
14032 tickableInputEl : function()
14034 if(!this.tickable || !this.editable){
14035 return this.inputEl();
14038 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14042 getAutoCreateTouchView : function()
14047 cls: 'form-group' //input-group
14053 type : this.inputType,
14054 cls : 'form-control x-combo-noedit',
14055 autocomplete: 'new-password',
14056 placeholder : this.placeholder || '',
14061 input.name = this.name;
14065 input.cls += ' input-' + this.size;
14068 if (this.disabled) {
14069 input.disabled = true;
14080 inputblock.cls += ' input-group';
14082 inputblock.cn.unshift({
14084 cls : 'input-group-addon',
14089 if(this.removable && !this.multiple){
14090 inputblock.cls += ' roo-removable';
14092 inputblock.cn.push({
14095 cls : 'roo-combo-removable-btn close'
14099 if(this.hasFeedback && !this.allowBlank){
14101 inputblock.cls += ' has-feedback';
14103 inputblock.cn.push({
14105 cls: 'glyphicon form-control-feedback'
14112 inputblock.cls += (this.before) ? '' : ' input-group';
14114 inputblock.cn.push({
14116 cls : 'input-group-addon',
14127 cls: 'form-hidden-field'
14141 cls: 'form-hidden-field'
14145 cls: 'roo-select2-choices',
14149 cls: 'roo-select2-search-field',
14162 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14168 if(!this.multiple && this.showToggleBtn){
14175 if (this.caret != false) {
14178 cls: 'fa fa-' + this.caret
14185 cls : 'input-group-addon btn dropdown-toggle',
14190 cls: 'combobox-clear',
14204 combobox.cls += ' roo-select2-container-multi';
14207 var align = this.labelAlign || this.parentLabelAlign();
14211 if(this.fieldLabel.length && this.labelWidth){
14213 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
14214 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
14219 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14220 tooltip : 'This field is required'
14224 cls : 'control-label ' + lw,
14225 html : this.fieldLabel
14236 if(this.indicatorpos == 'right'){
14240 cls : 'control-label ' + lw,
14241 html : this.fieldLabel
14246 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14247 tooltip : 'This field is required'
14259 var settings = this;
14261 ['xs','sm','md','lg'].map(function(size){
14262 if (settings[size]) {
14263 cfg.cls += ' col-' + size + '-' + settings[size];
14270 initTouchView : function()
14272 this.renderTouchView();
14274 this.touchViewEl.on('scroll', function(){
14275 this.el.dom.scrollTop = 0;
14278 this.originalValue = this.getValue();
14280 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14282 this.inputEl().on("click", this.showTouchView, this);
14283 if (this.triggerEl) {
14284 this.triggerEl.on("click", this.showTouchView, this);
14288 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14289 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14291 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14293 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14294 this.store.on('load', this.onTouchViewLoad, this);
14295 this.store.on('loadexception', this.onTouchViewLoadException, this);
14297 if(this.hiddenName){
14299 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14301 this.hiddenField.dom.value =
14302 this.hiddenValue !== undefined ? this.hiddenValue :
14303 this.value !== undefined ? this.value : '';
14305 this.el.dom.removeAttribute('name');
14306 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14310 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14311 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14314 if(this.removable && !this.multiple){
14315 var close = this.closeTriggerEl();
14317 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14318 close.on('click', this.removeBtnClick, this, close);
14322 * fix the bug in Safari iOS8
14324 this.inputEl().on("focus", function(e){
14325 document.activeElement.blur();
14333 renderTouchView : function()
14335 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14336 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14338 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14339 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14341 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14342 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14343 this.touchViewBodyEl.setStyle('overflow', 'auto');
14345 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14346 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14348 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14349 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14353 showTouchView : function()
14359 this.touchViewHeaderEl.hide();
14361 if(this.modalTitle.length){
14362 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14363 this.touchViewHeaderEl.show();
14366 this.touchViewEl.show();
14368 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14369 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14370 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14372 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14374 if(this.modalTitle.length){
14375 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14378 this.touchViewBodyEl.setHeight(bodyHeight);
14382 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14384 this.touchViewEl.addClass('in');
14387 this.doTouchViewQuery();
14391 hideTouchView : function()
14393 this.touchViewEl.removeClass('in');
14397 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14399 this.touchViewEl.setStyle('display', 'none');
14404 setTouchViewValue : function()
14411 Roo.each(this.tickItems, function(o){
14416 this.hideTouchView();
14419 doTouchViewQuery : function()
14428 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14432 if(!this.alwaysQuery || this.mode == 'local'){
14433 this.onTouchViewLoad();
14440 onTouchViewBeforeLoad : function(combo,opts)
14446 onTouchViewLoad : function()
14448 if(this.store.getCount() < 1){
14449 this.onTouchViewEmptyResults();
14453 this.clearTouchView();
14455 var rawValue = this.getRawValue();
14457 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14459 this.tickItems = [];
14461 this.store.data.each(function(d, rowIndex){
14462 var row = this.touchViewListGroup.createChild(template);
14464 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14465 row.addClass(d.data.cls);
14468 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14471 html : d.data[this.displayField]
14474 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14475 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14478 row.removeClass('selected');
14479 if(!this.multiple && this.valueField &&
14480 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14483 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14484 row.addClass('selected');
14487 if(this.multiple && this.valueField &&
14488 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14492 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14493 this.tickItems.push(d.data);
14496 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14500 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14502 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14504 if(this.modalTitle.length){
14505 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14508 var listHeight = this.touchViewListGroup.getHeight();
14512 if(firstChecked && listHeight > bodyHeight){
14513 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14518 onTouchViewLoadException : function()
14520 this.hideTouchView();
14523 onTouchViewEmptyResults : function()
14525 this.clearTouchView();
14527 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14529 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14533 clearTouchView : function()
14535 this.touchViewListGroup.dom.innerHTML = '';
14538 onTouchViewClick : function(e, el, o)
14540 e.preventDefault();
14543 var rowIndex = o.rowIndex;
14545 var r = this.store.getAt(rowIndex);
14547 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14549 if(!this.multiple){
14550 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14551 c.dom.removeAttribute('checked');
14554 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14556 this.setFromData(r.data);
14558 var close = this.closeTriggerEl();
14564 this.hideTouchView();
14566 this.fireEvent('select', this, r, rowIndex);
14571 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14572 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14573 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14577 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14578 this.addItem(r.data);
14579 this.tickItems.push(r.data);
14583 getAutoCreateNativeIOS : function()
14586 cls: 'form-group' //input-group,
14591 cls : 'roo-ios-select'
14595 combobox.name = this.name;
14598 if (this.disabled) {
14599 combobox.disabled = true;
14602 var settings = this;
14604 ['xs','sm','md','lg'].map(function(size){
14605 if (settings[size]) {
14606 cfg.cls += ' col-' + size + '-' + settings[size];
14616 initIOSView : function()
14618 this.store.on('load', this.onIOSViewLoad, this);
14623 onIOSViewLoad : function()
14625 if(this.store.getCount() < 1){
14629 this.clearIOSView();
14631 if(this.allowBlank) {
14633 var default_text = '-- SELECT --';
14635 var opt = this.inputEl().createChild({
14638 html : default_text
14642 o[this.valueField] = 0;
14643 o[this.displayField] = default_text;
14645 this.ios_options.push({
14652 this.store.data.each(function(d, rowIndex){
14656 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14657 html = d.data[this.displayField];
14662 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
14663 value = d.data[this.valueField];
14672 if(this.value == d.data[this.valueField]){
14673 option['selected'] = true;
14676 var opt = this.inputEl().createChild(option);
14678 this.ios_options.push({
14685 this.inputEl().on('change', function(){
14686 this.fireEvent('select', this);
14691 clearIOSView: function()
14693 this.inputEl().dom.innerHTML = '';
14695 this.ios_options = [];
14698 setIOSValue: function(v)
14702 if(!this.ios_options){
14706 Roo.each(this.ios_options, function(opts){
14708 opts.el.dom.removeAttribute('selected');
14710 if(opts.data[this.valueField] != v){
14714 opts.el.dom.setAttribute('selected', true);
14720 * @cfg {Boolean} grow
14724 * @cfg {Number} growMin
14728 * @cfg {Number} growMax
14737 Roo.apply(Roo.bootstrap.ComboBox, {
14741 cls: 'modal-header',
14763 cls: 'list-group-item',
14767 cls: 'roo-combobox-list-group-item-value'
14771 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14785 listItemCheckbox : {
14787 cls: 'list-group-item',
14791 cls: 'roo-combobox-list-group-item-value'
14795 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14811 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14816 cls: 'modal-footer',
14824 cls: 'col-xs-6 text-left',
14827 cls: 'btn btn-danger roo-touch-view-cancel',
14833 cls: 'col-xs-6 text-right',
14836 cls: 'btn btn-success roo-touch-view-ok',
14847 Roo.apply(Roo.bootstrap.ComboBox, {
14849 touchViewTemplate : {
14851 cls: 'modal fade roo-combobox-touch-view',
14855 cls: 'modal-dialog',
14856 style : 'position:fixed', // we have to fix position....
14860 cls: 'modal-content',
14862 Roo.bootstrap.ComboBox.header,
14863 Roo.bootstrap.ComboBox.body,
14864 Roo.bootstrap.ComboBox.footer
14873 * Ext JS Library 1.1.1
14874 * Copyright(c) 2006-2007, Ext JS, LLC.
14876 * Originally Released Under LGPL - original licence link has changed is not relivant.
14879 * <script type="text/javascript">
14884 * @extends Roo.util.Observable
14885 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
14886 * This class also supports single and multi selection modes. <br>
14887 * Create a data model bound view:
14889 var store = new Roo.data.Store(...);
14891 var view = new Roo.View({
14893 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
14895 singleSelect: true,
14896 selectedClass: "ydataview-selected",
14900 // listen for node click?
14901 view.on("click", function(vw, index, node, e){
14902 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14906 dataModel.load("foobar.xml");
14908 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14910 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14911 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14913 * Note: old style constructor is still suported (container, template, config)
14916 * Create a new View
14917 * @param {Object} config The config object
14920 Roo.View = function(config, depreciated_tpl, depreciated_config){
14922 this.parent = false;
14924 if (typeof(depreciated_tpl) == 'undefined') {
14925 // new way.. - universal constructor.
14926 Roo.apply(this, config);
14927 this.el = Roo.get(this.el);
14930 this.el = Roo.get(config);
14931 this.tpl = depreciated_tpl;
14932 Roo.apply(this, depreciated_config);
14934 this.wrapEl = this.el.wrap().wrap();
14935 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14938 if(typeof(this.tpl) == "string"){
14939 this.tpl = new Roo.Template(this.tpl);
14941 // support xtype ctors..
14942 this.tpl = new Roo.factory(this.tpl, Roo);
14946 this.tpl.compile();
14951 * @event beforeclick
14952 * Fires before a click is processed. Returns false to cancel the default action.
14953 * @param {Roo.View} this
14954 * @param {Number} index The index of the target node
14955 * @param {HTMLElement} node The target node
14956 * @param {Roo.EventObject} e The raw event object
14958 "beforeclick" : true,
14961 * Fires when a template node is clicked.
14962 * @param {Roo.View} this
14963 * @param {Number} index The index of the target node
14964 * @param {HTMLElement} node The target node
14965 * @param {Roo.EventObject} e The raw event object
14970 * Fires when a template node is double clicked.
14971 * @param {Roo.View} this
14972 * @param {Number} index The index of the target node
14973 * @param {HTMLElement} node The target node
14974 * @param {Roo.EventObject} e The raw event object
14978 * @event contextmenu
14979 * Fires when a template node is right clicked.
14980 * @param {Roo.View} this
14981 * @param {Number} index The index of the target node
14982 * @param {HTMLElement} node The target node
14983 * @param {Roo.EventObject} e The raw event object
14985 "contextmenu" : true,
14987 * @event selectionchange
14988 * Fires when the selected nodes change.
14989 * @param {Roo.View} this
14990 * @param {Array} selections Array of the selected nodes
14992 "selectionchange" : true,
14995 * @event beforeselect
14996 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14997 * @param {Roo.View} this
14998 * @param {HTMLElement} node The node to be selected
14999 * @param {Array} selections Array of currently selected nodes
15001 "beforeselect" : true,
15003 * @event preparedata
15004 * Fires on every row to render, to allow you to change the data.
15005 * @param {Roo.View} this
15006 * @param {Object} data to be rendered (change this)
15008 "preparedata" : true
15016 "click": this.onClick,
15017 "dblclick": this.onDblClick,
15018 "contextmenu": this.onContextMenu,
15022 this.selections = [];
15024 this.cmp = new Roo.CompositeElementLite([]);
15026 this.store = Roo.factory(this.store, Roo.data);
15027 this.setStore(this.store, true);
15030 if ( this.footer && this.footer.xtype) {
15032 var fctr = this.wrapEl.appendChild(document.createElement("div"));
15034 this.footer.dataSource = this.store;
15035 this.footer.container = fctr;
15036 this.footer = Roo.factory(this.footer, Roo);
15037 fctr.insertFirst(this.el);
15039 // this is a bit insane - as the paging toolbar seems to detach the el..
15040 // dom.parentNode.parentNode.parentNode
15041 // they get detached?
15045 Roo.View.superclass.constructor.call(this);
15050 Roo.extend(Roo.View, Roo.util.Observable, {
15053 * @cfg {Roo.data.Store} store Data store to load data from.
15058 * @cfg {String|Roo.Element} el The container element.
15063 * @cfg {String|Roo.Template} tpl The template used by this View
15067 * @cfg {String} dataName the named area of the template to use as the data area
15068 * Works with domtemplates roo-name="name"
15072 * @cfg {String} selectedClass The css class to add to selected nodes
15074 selectedClass : "x-view-selected",
15076 * @cfg {String} emptyText The empty text to show when nothing is loaded.
15081 * @cfg {String} text to display on mask (default Loading)
15085 * @cfg {Boolean} multiSelect Allow multiple selection
15087 multiSelect : false,
15089 * @cfg {Boolean} singleSelect Allow single selection
15091 singleSelect: false,
15094 * @cfg {Boolean} toggleSelect - selecting
15096 toggleSelect : false,
15099 * @cfg {Boolean} tickable - selecting
15104 * Returns the element this view is bound to.
15105 * @return {Roo.Element}
15107 getEl : function(){
15108 return this.wrapEl;
15114 * Refreshes the view. - called by datachanged on the store. - do not call directly.
15116 refresh : function(){
15117 //Roo.log('refresh');
15120 // if we are using something like 'domtemplate', then
15121 // the what gets used is:
15122 // t.applySubtemplate(NAME, data, wrapping data..)
15123 // the outer template then get' applied with
15124 // the store 'extra data'
15125 // and the body get's added to the
15126 // roo-name="data" node?
15127 // <span class='roo-tpl-{name}'></span> ?????
15131 this.clearSelections();
15132 this.el.update("");
15134 var records = this.store.getRange();
15135 if(records.length < 1) {
15137 // is this valid?? = should it render a template??
15139 this.el.update(this.emptyText);
15143 if (this.dataName) {
15144 this.el.update(t.apply(this.store.meta)); //????
15145 el = this.el.child('.roo-tpl-' + this.dataName);
15148 for(var i = 0, len = records.length; i < len; i++){
15149 var data = this.prepareData(records[i].data, i, records[i]);
15150 this.fireEvent("preparedata", this, data, i, records[i]);
15152 var d = Roo.apply({}, data);
15155 Roo.apply(d, {'roo-id' : Roo.id()});
15159 Roo.each(this.parent.item, function(item){
15160 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15163 Roo.apply(d, {'roo-data-checked' : 'checked'});
15167 html[html.length] = Roo.util.Format.trim(
15169 t.applySubtemplate(this.dataName, d, this.store.meta) :
15176 el.update(html.join(""));
15177 this.nodes = el.dom.childNodes;
15178 this.updateIndexes(0);
15183 * Function to override to reformat the data that is sent to
15184 * the template for each node.
15185 * DEPRICATED - use the preparedata event handler.
15186 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15187 * a JSON object for an UpdateManager bound view).
15189 prepareData : function(data, index, record)
15191 this.fireEvent("preparedata", this, data, index, record);
15195 onUpdate : function(ds, record){
15196 // Roo.log('on update');
15197 this.clearSelections();
15198 var index = this.store.indexOf(record);
15199 var n = this.nodes[index];
15200 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15201 n.parentNode.removeChild(n);
15202 this.updateIndexes(index, index);
15208 onAdd : function(ds, records, index)
15210 //Roo.log(['on Add', ds, records, index] );
15211 this.clearSelections();
15212 if(this.nodes.length == 0){
15216 var n = this.nodes[index];
15217 for(var i = 0, len = records.length; i < len; i++){
15218 var d = this.prepareData(records[i].data, i, records[i]);
15220 this.tpl.insertBefore(n, d);
15223 this.tpl.append(this.el, d);
15226 this.updateIndexes(index);
15229 onRemove : function(ds, record, index){
15230 // Roo.log('onRemove');
15231 this.clearSelections();
15232 var el = this.dataName ?
15233 this.el.child('.roo-tpl-' + this.dataName) :
15236 el.dom.removeChild(this.nodes[index]);
15237 this.updateIndexes(index);
15241 * Refresh an individual node.
15242 * @param {Number} index
15244 refreshNode : function(index){
15245 this.onUpdate(this.store, this.store.getAt(index));
15248 updateIndexes : function(startIndex, endIndex){
15249 var ns = this.nodes;
15250 startIndex = startIndex || 0;
15251 endIndex = endIndex || ns.length - 1;
15252 for(var i = startIndex; i <= endIndex; i++){
15253 ns[i].nodeIndex = i;
15258 * Changes the data store this view uses and refresh the view.
15259 * @param {Store} store
15261 setStore : function(store, initial){
15262 if(!initial && this.store){
15263 this.store.un("datachanged", this.refresh);
15264 this.store.un("add", this.onAdd);
15265 this.store.un("remove", this.onRemove);
15266 this.store.un("update", this.onUpdate);
15267 this.store.un("clear", this.refresh);
15268 this.store.un("beforeload", this.onBeforeLoad);
15269 this.store.un("load", this.onLoad);
15270 this.store.un("loadexception", this.onLoad);
15274 store.on("datachanged", this.refresh, this);
15275 store.on("add", this.onAdd, this);
15276 store.on("remove", this.onRemove, this);
15277 store.on("update", this.onUpdate, this);
15278 store.on("clear", this.refresh, this);
15279 store.on("beforeload", this.onBeforeLoad, this);
15280 store.on("load", this.onLoad, this);
15281 store.on("loadexception", this.onLoad, this);
15289 * onbeforeLoad - masks the loading area.
15292 onBeforeLoad : function(store,opts)
15294 //Roo.log('onBeforeLoad');
15296 this.el.update("");
15298 this.el.mask(this.mask ? this.mask : "Loading" );
15300 onLoad : function ()
15307 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15308 * @param {HTMLElement} node
15309 * @return {HTMLElement} The template node
15311 findItemFromChild : function(node){
15312 var el = this.dataName ?
15313 this.el.child('.roo-tpl-' + this.dataName,true) :
15316 if(!node || node.parentNode == el){
15319 var p = node.parentNode;
15320 while(p && p != el){
15321 if(p.parentNode == el){
15330 onClick : function(e){
15331 var item = this.findItemFromChild(e.getTarget());
15333 var index = this.indexOf(item);
15334 if(this.onItemClick(item, index, e) !== false){
15335 this.fireEvent("click", this, index, item, e);
15338 this.clearSelections();
15343 onContextMenu : function(e){
15344 var item = this.findItemFromChild(e.getTarget());
15346 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15351 onDblClick : function(e){
15352 var item = this.findItemFromChild(e.getTarget());
15354 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15358 onItemClick : function(item, index, e)
15360 if(this.fireEvent("beforeclick", this, index, item, e) === false){
15363 if (this.toggleSelect) {
15364 var m = this.isSelected(item) ? 'unselect' : 'select';
15367 _t[m](item, true, false);
15370 if(this.multiSelect || this.singleSelect){
15371 if(this.multiSelect && e.shiftKey && this.lastSelection){
15372 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15374 this.select(item, this.multiSelect && e.ctrlKey);
15375 this.lastSelection = item;
15378 if(!this.tickable){
15379 e.preventDefault();
15387 * Get the number of selected nodes.
15390 getSelectionCount : function(){
15391 return this.selections.length;
15395 * Get the currently selected nodes.
15396 * @return {Array} An array of HTMLElements
15398 getSelectedNodes : function(){
15399 return this.selections;
15403 * Get the indexes of the selected nodes.
15406 getSelectedIndexes : function(){
15407 var indexes = [], s = this.selections;
15408 for(var i = 0, len = s.length; i < len; i++){
15409 indexes.push(s[i].nodeIndex);
15415 * Clear all selections
15416 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15418 clearSelections : function(suppressEvent){
15419 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15420 this.cmp.elements = this.selections;
15421 this.cmp.removeClass(this.selectedClass);
15422 this.selections = [];
15423 if(!suppressEvent){
15424 this.fireEvent("selectionchange", this, this.selections);
15430 * Returns true if the passed node is selected
15431 * @param {HTMLElement/Number} node The node or node index
15432 * @return {Boolean}
15434 isSelected : function(node){
15435 var s = this.selections;
15439 node = this.getNode(node);
15440 return s.indexOf(node) !== -1;
15445 * @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
15446 * @param {Boolean} keepExisting (optional) true to keep existing selections
15447 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15449 select : function(nodeInfo, keepExisting, suppressEvent){
15450 if(nodeInfo instanceof Array){
15452 this.clearSelections(true);
15454 for(var i = 0, len = nodeInfo.length; i < len; i++){
15455 this.select(nodeInfo[i], true, true);
15459 var node = this.getNode(nodeInfo);
15460 if(!node || this.isSelected(node)){
15461 return; // already selected.
15464 this.clearSelections(true);
15467 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15468 Roo.fly(node).addClass(this.selectedClass);
15469 this.selections.push(node);
15470 if(!suppressEvent){
15471 this.fireEvent("selectionchange", this, this.selections);
15479 * @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
15480 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15481 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15483 unselect : function(nodeInfo, keepExisting, suppressEvent)
15485 if(nodeInfo instanceof Array){
15486 Roo.each(this.selections, function(s) {
15487 this.unselect(s, nodeInfo);
15491 var node = this.getNode(nodeInfo);
15492 if(!node || !this.isSelected(node)){
15493 //Roo.log("not selected");
15494 return; // not selected.
15498 Roo.each(this.selections, function(s) {
15500 Roo.fly(node).removeClass(this.selectedClass);
15507 this.selections= ns;
15508 this.fireEvent("selectionchange", this, this.selections);
15512 * Gets a template node.
15513 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15514 * @return {HTMLElement} The node or null if it wasn't found
15516 getNode : function(nodeInfo){
15517 if(typeof nodeInfo == "string"){
15518 return document.getElementById(nodeInfo);
15519 }else if(typeof nodeInfo == "number"){
15520 return this.nodes[nodeInfo];
15526 * Gets a range template nodes.
15527 * @param {Number} startIndex
15528 * @param {Number} endIndex
15529 * @return {Array} An array of nodes
15531 getNodes : function(start, end){
15532 var ns = this.nodes;
15533 start = start || 0;
15534 end = typeof end == "undefined" ? ns.length - 1 : end;
15537 for(var i = start; i <= end; i++){
15541 for(var i = start; i >= end; i--){
15549 * Finds the index of the passed node
15550 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15551 * @return {Number} The index of the node or -1
15553 indexOf : function(node){
15554 node = this.getNode(node);
15555 if(typeof node.nodeIndex == "number"){
15556 return node.nodeIndex;
15558 var ns = this.nodes;
15559 for(var i = 0, len = ns.length; i < len; i++){
15570 * based on jquery fullcalendar
15574 Roo.bootstrap = Roo.bootstrap || {};
15576 * @class Roo.bootstrap.Calendar
15577 * @extends Roo.bootstrap.Component
15578 * Bootstrap Calendar class
15579 * @cfg {Boolean} loadMask (true|false) default false
15580 * @cfg {Object} header generate the user specific header of the calendar, default false
15583 * Create a new Container
15584 * @param {Object} config The config object
15589 Roo.bootstrap.Calendar = function(config){
15590 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15594 * Fires when a date is selected
15595 * @param {DatePicker} this
15596 * @param {Date} date The selected date
15600 * @event monthchange
15601 * Fires when the displayed month changes
15602 * @param {DatePicker} this
15603 * @param {Date} date The selected month
15605 'monthchange': true,
15607 * @event evententer
15608 * Fires when mouse over an event
15609 * @param {Calendar} this
15610 * @param {event} Event
15612 'evententer': true,
15614 * @event eventleave
15615 * Fires when the mouse leaves an
15616 * @param {Calendar} this
15619 'eventleave': true,
15621 * @event eventclick
15622 * Fires when the mouse click an
15623 * @param {Calendar} this
15632 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
15635 * @cfg {Number} startDay
15636 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15644 getAutoCreate : function(){
15647 var fc_button = function(name, corner, style, content ) {
15648 return Roo.apply({},{
15650 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
15652 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15655 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15666 style : 'width:100%',
15673 cls : 'fc-header-left',
15675 fc_button('prev', 'left', 'arrow', '‹' ),
15676 fc_button('next', 'right', 'arrow', '›' ),
15677 { tag: 'span', cls: 'fc-header-space' },
15678 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
15686 cls : 'fc-header-center',
15690 cls: 'fc-header-title',
15693 html : 'month / year'
15701 cls : 'fc-header-right',
15703 /* fc_button('month', 'left', '', 'month' ),
15704 fc_button('week', '', '', 'week' ),
15705 fc_button('day', 'right', '', 'day' )
15717 header = this.header;
15720 var cal_heads = function() {
15722 // fixme - handle this.
15724 for (var i =0; i < Date.dayNames.length; i++) {
15725 var d = Date.dayNames[i];
15728 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15729 html : d.substring(0,3)
15733 ret[0].cls += ' fc-first';
15734 ret[6].cls += ' fc-last';
15737 var cal_cell = function(n) {
15740 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15745 cls: 'fc-day-number',
15749 cls: 'fc-day-content',
15753 style: 'position: relative;' // height: 17px;
15765 var cal_rows = function() {
15768 for (var r = 0; r < 6; r++) {
15775 for (var i =0; i < Date.dayNames.length; i++) {
15776 var d = Date.dayNames[i];
15777 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15780 row.cn[0].cls+=' fc-first';
15781 row.cn[0].cn[0].style = 'min-height:90px';
15782 row.cn[6].cls+=' fc-last';
15786 ret[0].cls += ' fc-first';
15787 ret[4].cls += ' fc-prev-last';
15788 ret[5].cls += ' fc-last';
15795 cls: 'fc-border-separate',
15796 style : 'width:100%',
15804 cls : 'fc-first fc-last',
15822 cls : 'fc-content',
15823 style : "position: relative;",
15826 cls : 'fc-view fc-view-month fc-grid',
15827 style : 'position: relative',
15828 unselectable : 'on',
15831 cls : 'fc-event-container',
15832 style : 'position:absolute;z-index:8;top:0;left:0;'
15850 initEvents : function()
15853 throw "can not find store for calendar";
15859 style: "text-align:center",
15863 style: "background-color:white;width:50%;margin:250 auto",
15867 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
15878 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15880 var size = this.el.select('.fc-content', true).first().getSize();
15881 this.maskEl.setSize(size.width, size.height);
15882 this.maskEl.enableDisplayMode("block");
15883 if(!this.loadMask){
15884 this.maskEl.hide();
15887 this.store = Roo.factory(this.store, Roo.data);
15888 this.store.on('load', this.onLoad, this);
15889 this.store.on('beforeload', this.onBeforeLoad, this);
15893 this.cells = this.el.select('.fc-day',true);
15894 //Roo.log(this.cells);
15895 this.textNodes = this.el.query('.fc-day-number');
15896 this.cells.addClassOnOver('fc-state-hover');
15898 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15899 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15900 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15901 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15903 this.on('monthchange', this.onMonthChange, this);
15905 this.update(new Date().clearTime());
15908 resize : function() {
15909 var sz = this.el.getSize();
15911 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15912 this.el.select('.fc-day-content div',true).setHeight(34);
15917 showPrevMonth : function(e){
15918 this.update(this.activeDate.add("mo", -1));
15920 showToday : function(e){
15921 this.update(new Date().clearTime());
15924 showNextMonth : function(e){
15925 this.update(this.activeDate.add("mo", 1));
15929 showPrevYear : function(){
15930 this.update(this.activeDate.add("y", -1));
15934 showNextYear : function(){
15935 this.update(this.activeDate.add("y", 1));
15940 update : function(date)
15942 var vd = this.activeDate;
15943 this.activeDate = date;
15944 // if(vd && this.el){
15945 // var t = date.getTime();
15946 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15947 // Roo.log('using add remove');
15949 // this.fireEvent('monthchange', this, date);
15951 // this.cells.removeClass("fc-state-highlight");
15952 // this.cells.each(function(c){
15953 // if(c.dateValue == t){
15954 // c.addClass("fc-state-highlight");
15955 // setTimeout(function(){
15956 // try{c.dom.firstChild.focus();}catch(e){}
15966 var days = date.getDaysInMonth();
15968 var firstOfMonth = date.getFirstDateOfMonth();
15969 var startingPos = firstOfMonth.getDay()-this.startDay;
15971 if(startingPos < this.startDay){
15975 var pm = date.add(Date.MONTH, -1);
15976 var prevStart = pm.getDaysInMonth()-startingPos;
15978 this.cells = this.el.select('.fc-day',true);
15979 this.textNodes = this.el.query('.fc-day-number');
15980 this.cells.addClassOnOver('fc-state-hover');
15982 var cells = this.cells.elements;
15983 var textEls = this.textNodes;
15985 Roo.each(cells, function(cell){
15986 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15989 days += startingPos;
15991 // convert everything to numbers so it's fast
15992 var day = 86400000;
15993 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15996 //Roo.log(prevStart);
15998 var today = new Date().clearTime().getTime();
15999 var sel = date.clearTime().getTime();
16000 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16001 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16002 var ddMatch = this.disabledDatesRE;
16003 var ddText = this.disabledDatesText;
16004 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16005 var ddaysText = this.disabledDaysText;
16006 var format = this.format;
16008 var setCellClass = function(cal, cell){
16012 //Roo.log('set Cell Class');
16014 var t = d.getTime();
16018 cell.dateValue = t;
16020 cell.className += " fc-today";
16021 cell.className += " fc-state-highlight";
16022 cell.title = cal.todayText;
16025 // disable highlight in other month..
16026 //cell.className += " fc-state-highlight";
16031 cell.className = " fc-state-disabled";
16032 cell.title = cal.minText;
16036 cell.className = " fc-state-disabled";
16037 cell.title = cal.maxText;
16041 if(ddays.indexOf(d.getDay()) != -1){
16042 cell.title = ddaysText;
16043 cell.className = " fc-state-disabled";
16046 if(ddMatch && format){
16047 var fvalue = d.dateFormat(format);
16048 if(ddMatch.test(fvalue)){
16049 cell.title = ddText.replace("%0", fvalue);
16050 cell.className = " fc-state-disabled";
16054 if (!cell.initialClassName) {
16055 cell.initialClassName = cell.dom.className;
16058 cell.dom.className = cell.initialClassName + ' ' + cell.className;
16063 for(; i < startingPos; i++) {
16064 textEls[i].innerHTML = (++prevStart);
16065 d.setDate(d.getDate()+1);
16067 cells[i].className = "fc-past fc-other-month";
16068 setCellClass(this, cells[i]);
16073 for(; i < days; i++){
16074 intDay = i - startingPos + 1;
16075 textEls[i].innerHTML = (intDay);
16076 d.setDate(d.getDate()+1);
16078 cells[i].className = ''; // "x-date-active";
16079 setCellClass(this, cells[i]);
16083 for(; i < 42; i++) {
16084 textEls[i].innerHTML = (++extraDays);
16085 d.setDate(d.getDate()+1);
16087 cells[i].className = "fc-future fc-other-month";
16088 setCellClass(this, cells[i]);
16091 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16093 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16095 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16096 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16098 if(totalRows != 6){
16099 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16100 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16103 this.fireEvent('monthchange', this, date);
16107 if(!this.internalRender){
16108 var main = this.el.dom.firstChild;
16109 var w = main.offsetWidth;
16110 this.el.setWidth(w + this.el.getBorderWidth("lr"));
16111 Roo.fly(main).setWidth(w);
16112 this.internalRender = true;
16113 // opera does not respect the auto grow header center column
16114 // then, after it gets a width opera refuses to recalculate
16115 // without a second pass
16116 if(Roo.isOpera && !this.secondPass){
16117 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16118 this.secondPass = true;
16119 this.update.defer(10, this, [date]);
16126 findCell : function(dt) {
16127 dt = dt.clearTime().getTime();
16129 this.cells.each(function(c){
16130 //Roo.log("check " +c.dateValue + '?=' + dt);
16131 if(c.dateValue == dt){
16141 findCells : function(ev) {
16142 var s = ev.start.clone().clearTime().getTime();
16144 var e= ev.end.clone().clearTime().getTime();
16147 this.cells.each(function(c){
16148 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16150 if(c.dateValue > e){
16153 if(c.dateValue < s){
16162 // findBestRow: function(cells)
16166 // for (var i =0 ; i < cells.length;i++) {
16167 // ret = Math.max(cells[i].rows || 0,ret);
16174 addItem : function(ev)
16176 // look for vertical location slot in
16177 var cells = this.findCells(ev);
16179 // ev.row = this.findBestRow(cells);
16181 // work out the location.
16185 for(var i =0; i < cells.length; i++) {
16187 cells[i].row = cells[0].row;
16190 cells[i].row = cells[i].row + 1;
16200 if (crow.start.getY() == cells[i].getY()) {
16202 crow.end = cells[i];
16219 cells[0].events.push(ev);
16221 this.calevents.push(ev);
16224 clearEvents: function() {
16226 if(!this.calevents){
16230 Roo.each(this.cells.elements, function(c){
16236 Roo.each(this.calevents, function(e) {
16237 Roo.each(e.els, function(el) {
16238 el.un('mouseenter' ,this.onEventEnter, this);
16239 el.un('mouseleave' ,this.onEventLeave, this);
16244 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16250 renderEvents: function()
16254 this.cells.each(function(c) {
16263 if(c.row != c.events.length){
16264 r = 4 - (4 - (c.row - c.events.length));
16267 c.events = ev.slice(0, r);
16268 c.more = ev.slice(r);
16270 if(c.more.length && c.more.length == 1){
16271 c.events.push(c.more.pop());
16274 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16278 this.cells.each(function(c) {
16280 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16283 for (var e = 0; e < c.events.length; e++){
16284 var ev = c.events[e];
16285 var rows = ev.rows;
16287 for(var i = 0; i < rows.length; i++) {
16289 // how many rows should it span..
16292 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16293 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16295 unselectable : "on",
16298 cls: 'fc-event-inner',
16302 // cls: 'fc-event-time',
16303 // html : cells.length > 1 ? '' : ev.time
16307 cls: 'fc-event-title',
16308 html : String.format('{0}', ev.title)
16315 cls: 'ui-resizable-handle ui-resizable-e',
16316 html : '  '
16323 cfg.cls += ' fc-event-start';
16325 if ((i+1) == rows.length) {
16326 cfg.cls += ' fc-event-end';
16329 var ctr = _this.el.select('.fc-event-container',true).first();
16330 var cg = ctr.createChild(cfg);
16332 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16333 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16335 var r = (c.more.length) ? 1 : 0;
16336 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
16337 cg.setWidth(ebox.right - sbox.x -2);
16339 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16340 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16341 cg.on('click', _this.onEventClick, _this, ev);
16352 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16353 style : 'position: absolute',
16354 unselectable : "on",
16357 cls: 'fc-event-inner',
16361 cls: 'fc-event-title',
16369 cls: 'ui-resizable-handle ui-resizable-e',
16370 html : '  '
16376 var ctr = _this.el.select('.fc-event-container',true).first();
16377 var cg = ctr.createChild(cfg);
16379 var sbox = c.select('.fc-day-content',true).first().getBox();
16380 var ebox = c.select('.fc-day-content',true).first().getBox();
16382 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
16383 cg.setWidth(ebox.right - sbox.x -2);
16385 cg.on('click', _this.onMoreEventClick, _this, c.more);
16395 onEventEnter: function (e, el,event,d) {
16396 this.fireEvent('evententer', this, el, event);
16399 onEventLeave: function (e, el,event,d) {
16400 this.fireEvent('eventleave', this, el, event);
16403 onEventClick: function (e, el,event,d) {
16404 this.fireEvent('eventclick', this, el, event);
16407 onMonthChange: function () {
16411 onMoreEventClick: function(e, el, more)
16415 this.calpopover.placement = 'right';
16416 this.calpopover.setTitle('More');
16418 this.calpopover.setContent('');
16420 var ctr = this.calpopover.el.select('.popover-content', true).first();
16422 Roo.each(more, function(m){
16424 cls : 'fc-event-hori fc-event-draggable',
16427 var cg = ctr.createChild(cfg);
16429 cg.on('click', _this.onEventClick, _this, m);
16432 this.calpopover.show(el);
16437 onLoad: function ()
16439 this.calevents = [];
16442 if(this.store.getCount() > 0){
16443 this.store.data.each(function(d){
16446 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16447 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16448 time : d.data.start_time,
16449 title : d.data.title,
16450 description : d.data.description,
16451 venue : d.data.venue
16456 this.renderEvents();
16458 if(this.calevents.length && this.loadMask){
16459 this.maskEl.hide();
16463 onBeforeLoad: function()
16465 this.clearEvents();
16467 this.maskEl.show();
16481 * @class Roo.bootstrap.Popover
16482 * @extends Roo.bootstrap.Component
16483 * Bootstrap Popover class
16484 * @cfg {String} html contents of the popover (or false to use children..)
16485 * @cfg {String} title of popover (or false to hide)
16486 * @cfg {String} placement how it is placed
16487 * @cfg {String} trigger click || hover (or false to trigger manually)
16488 * @cfg {String} over what (parent or false to trigger manually.)
16489 * @cfg {Number} delay - delay before showing
16492 * Create a new Popover
16493 * @param {Object} config The config object
16496 Roo.bootstrap.Popover = function(config){
16497 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16503 * After the popover show
16505 * @param {Roo.bootstrap.Popover} this
16510 * After the popover hide
16512 * @param {Roo.bootstrap.Popover} this
16518 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
16520 title: 'Fill in a title',
16523 placement : 'right',
16524 trigger : 'hover', // hover
16530 can_build_overlaid : false,
16532 getChildContainer : function()
16534 return this.el.select('.popover-content',true).first();
16537 getAutoCreate : function(){
16540 cls : 'popover roo-dynamic',
16541 style: 'display:block',
16547 cls : 'popover-inner',
16551 cls: 'popover-title',
16555 cls : 'popover-content',
16566 setTitle: function(str)
16569 this.el.select('.popover-title',true).first().dom.innerHTML = str;
16571 setContent: function(str)
16574 this.el.select('.popover-content',true).first().dom.innerHTML = str;
16576 // as it get's added to the bottom of the page.
16577 onRender : function(ct, position)
16579 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16581 var cfg = Roo.apply({}, this.getAutoCreate());
16585 cfg.cls += ' ' + this.cls;
16588 cfg.style = this.style;
16590 //Roo.log("adding to ");
16591 this.el = Roo.get(document.body).createChild(cfg, position);
16592 // Roo.log(this.el);
16597 initEvents : function()
16599 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16600 this.el.enableDisplayMode('block');
16602 if (this.over === false) {
16605 if (this.triggers === false) {
16608 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16609 var triggers = this.trigger ? this.trigger.split(' ') : [];
16610 Roo.each(triggers, function(trigger) {
16612 if (trigger == 'click') {
16613 on_el.on('click', this.toggle, this);
16614 } else if (trigger != 'manual') {
16615 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
16616 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16618 on_el.on(eventIn ,this.enter, this);
16619 on_el.on(eventOut, this.leave, this);
16630 toggle : function () {
16631 this.hoverState == 'in' ? this.leave() : this.enter();
16634 enter : function () {
16636 clearTimeout(this.timeout);
16638 this.hoverState = 'in';
16640 if (!this.delay || !this.delay.show) {
16645 this.timeout = setTimeout(function () {
16646 if (_t.hoverState == 'in') {
16649 }, this.delay.show)
16652 leave : function() {
16653 clearTimeout(this.timeout);
16655 this.hoverState = 'out';
16657 if (!this.delay || !this.delay.hide) {
16662 this.timeout = setTimeout(function () {
16663 if (_t.hoverState == 'out') {
16666 }, this.delay.hide)
16669 show : function (on_el)
16672 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16676 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16677 if (this.html !== false) {
16678 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16680 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16681 if (!this.title.length) {
16682 this.el.select('.popover-title',true).hide();
16685 var placement = typeof this.placement == 'function' ?
16686 this.placement.call(this, this.el, on_el) :
16689 var autoToken = /\s?auto?\s?/i;
16690 var autoPlace = autoToken.test(placement);
16692 placement = placement.replace(autoToken, '') || 'top';
16696 //this.el.setXY([0,0]);
16698 this.el.dom.style.display='block';
16699 this.el.addClass(placement);
16701 //this.el.appendTo(on_el);
16703 var p = this.getPosition();
16704 var box = this.el.getBox();
16709 var align = Roo.bootstrap.Popover.alignment[placement];
16710 this.el.alignTo(on_el, align[0],align[1]);
16711 //var arrow = this.el.select('.arrow',true).first();
16712 //arrow.set(align[2],
16714 this.el.addClass('in');
16717 if (this.el.hasClass('fade')) {
16721 this.hoverState = 'in';
16723 this.fireEvent('show', this);
16728 this.el.setXY([0,0]);
16729 this.el.removeClass('in');
16731 this.hoverState = null;
16733 this.fireEvent('hide', this);
16738 Roo.bootstrap.Popover.alignment = {
16739 'left' : ['r-l', [-10,0], 'right'],
16740 'right' : ['l-r', [10,0], 'left'],
16741 'bottom' : ['t-b', [0,10], 'top'],
16742 'top' : [ 'b-t', [0,-10], 'bottom']
16753 * @class Roo.bootstrap.Progress
16754 * @extends Roo.bootstrap.Component
16755 * Bootstrap Progress class
16756 * @cfg {Boolean} striped striped of the progress bar
16757 * @cfg {Boolean} active animated of the progress bar
16761 * Create a new Progress
16762 * @param {Object} config The config object
16765 Roo.bootstrap.Progress = function(config){
16766 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16769 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
16774 getAutoCreate : function(){
16782 cfg.cls += ' progress-striped';
16786 cfg.cls += ' active';
16805 * @class Roo.bootstrap.ProgressBar
16806 * @extends Roo.bootstrap.Component
16807 * Bootstrap ProgressBar class
16808 * @cfg {Number} aria_valuenow aria-value now
16809 * @cfg {Number} aria_valuemin aria-value min
16810 * @cfg {Number} aria_valuemax aria-value max
16811 * @cfg {String} label label for the progress bar
16812 * @cfg {String} panel (success | info | warning | danger )
16813 * @cfg {String} role role of the progress bar
16814 * @cfg {String} sr_only text
16818 * Create a new ProgressBar
16819 * @param {Object} config The config object
16822 Roo.bootstrap.ProgressBar = function(config){
16823 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16826 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
16830 aria_valuemax : 100,
16836 getAutoCreate : function()
16841 cls: 'progress-bar',
16842 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16854 cfg.role = this.role;
16857 if(this.aria_valuenow){
16858 cfg['aria-valuenow'] = this.aria_valuenow;
16861 if(this.aria_valuemin){
16862 cfg['aria-valuemin'] = this.aria_valuemin;
16865 if(this.aria_valuemax){
16866 cfg['aria-valuemax'] = this.aria_valuemax;
16869 if(this.label && !this.sr_only){
16870 cfg.html = this.label;
16874 cfg.cls += ' progress-bar-' + this.panel;
16880 update : function(aria_valuenow)
16882 this.aria_valuenow = aria_valuenow;
16884 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16899 * @class Roo.bootstrap.TabGroup
16900 * @extends Roo.bootstrap.Column
16901 * Bootstrap Column class
16902 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16903 * @cfg {Boolean} carousel true to make the group behave like a carousel
16904 * @cfg {Boolean} bullets show bullets for the panels
16905 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16906 * @cfg {Number} timer auto slide timer .. default 0 millisecond
16907 * @cfg {Boolean} showarrow (true|false) show arrow default true
16910 * Create a new TabGroup
16911 * @param {Object} config The config object
16914 Roo.bootstrap.TabGroup = function(config){
16915 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16917 this.navId = Roo.id();
16920 Roo.bootstrap.TabGroup.register(this);
16924 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16927 transition : false,
16932 slideOnTouch : false,
16935 getAutoCreate : function()
16937 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16939 cfg.cls += ' tab-content';
16941 if (this.carousel) {
16942 cfg.cls += ' carousel slide';
16945 cls : 'carousel-inner',
16949 if(this.bullets && !Roo.isTouch){
16952 cls : 'carousel-bullets',
16956 if(this.bullets_cls){
16957 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16964 cfg.cn[0].cn.push(bullets);
16967 if(this.showarrow){
16968 cfg.cn[0].cn.push({
16970 class : 'carousel-arrow',
16974 class : 'carousel-prev',
16978 class : 'fa fa-chevron-left'
16984 class : 'carousel-next',
16988 class : 'fa fa-chevron-right'
17001 initEvents: function()
17003 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17004 // this.el.on("touchstart", this.onTouchStart, this);
17007 if(this.autoslide){
17010 this.slideFn = window.setInterval(function() {
17011 _this.showPanelNext();
17015 if(this.showarrow){
17016 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17017 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17023 // onTouchStart : function(e, el, o)
17025 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17029 // this.showPanelNext();
17033 getChildContainer : function()
17035 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17039 * register a Navigation item
17040 * @param {Roo.bootstrap.NavItem} the navitem to add
17042 register : function(item)
17044 this.tabs.push( item);
17045 item.navId = this.navId; // not really needed..
17050 getActivePanel : function()
17053 Roo.each(this.tabs, function(t) {
17063 getPanelByName : function(n)
17066 Roo.each(this.tabs, function(t) {
17067 if (t.tabId == n) {
17075 indexOfPanel : function(p)
17078 Roo.each(this.tabs, function(t,i) {
17079 if (t.tabId == p.tabId) {
17088 * show a specific panel
17089 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17090 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17092 showPanel : function (pan)
17094 if(this.transition || typeof(pan) == 'undefined'){
17095 Roo.log("waiting for the transitionend");
17099 if (typeof(pan) == 'number') {
17100 pan = this.tabs[pan];
17103 if (typeof(pan) == 'string') {
17104 pan = this.getPanelByName(pan);
17107 var cur = this.getActivePanel();
17110 Roo.log('pan or acitve pan is undefined');
17114 if (pan.tabId == this.getActivePanel().tabId) {
17118 if (false === cur.fireEvent('beforedeactivate')) {
17122 if(this.bullets > 0 && !Roo.isTouch){
17123 this.setActiveBullet(this.indexOfPanel(pan));
17126 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17128 this.transition = true;
17129 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
17130 var lr = dir == 'next' ? 'left' : 'right';
17131 pan.el.addClass(dir); // or prev
17132 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17133 cur.el.addClass(lr); // or right
17134 pan.el.addClass(lr);
17137 cur.el.on('transitionend', function() {
17138 Roo.log("trans end?");
17140 pan.el.removeClass([lr,dir]);
17141 pan.setActive(true);
17143 cur.el.removeClass([lr]);
17144 cur.setActive(false);
17146 _this.transition = false;
17148 }, this, { single: true } );
17153 cur.setActive(false);
17154 pan.setActive(true);
17159 showPanelNext : function()
17161 var i = this.indexOfPanel(this.getActivePanel());
17163 if (i >= this.tabs.length - 1 && !this.autoslide) {
17167 if (i >= this.tabs.length - 1 && this.autoslide) {
17171 this.showPanel(this.tabs[i+1]);
17174 showPanelPrev : function()
17176 var i = this.indexOfPanel(this.getActivePanel());
17178 if (i < 1 && !this.autoslide) {
17182 if (i < 1 && this.autoslide) {
17183 i = this.tabs.length;
17186 this.showPanel(this.tabs[i-1]);
17190 addBullet: function()
17192 if(!this.bullets || Roo.isTouch){
17195 var ctr = this.el.select('.carousel-bullets',true).first();
17196 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17197 var bullet = ctr.createChild({
17198 cls : 'bullet bullet-' + i
17199 },ctr.dom.lastChild);
17204 bullet.on('click', (function(e, el, o, ii, t){
17206 e.preventDefault();
17208 this.showPanel(ii);
17210 if(this.autoslide && this.slideFn){
17211 clearInterval(this.slideFn);
17212 this.slideFn = window.setInterval(function() {
17213 _this.showPanelNext();
17217 }).createDelegate(this, [i, bullet], true));
17222 setActiveBullet : function(i)
17228 Roo.each(this.el.select('.bullet', true).elements, function(el){
17229 el.removeClass('selected');
17232 var bullet = this.el.select('.bullet-' + i, true).first();
17238 bullet.addClass('selected');
17249 Roo.apply(Roo.bootstrap.TabGroup, {
17253 * register a Navigation Group
17254 * @param {Roo.bootstrap.NavGroup} the navgroup to add
17256 register : function(navgrp)
17258 this.groups[navgrp.navId] = navgrp;
17262 * fetch a Navigation Group based on the navigation ID
17263 * if one does not exist , it will get created.
17264 * @param {string} the navgroup to add
17265 * @returns {Roo.bootstrap.NavGroup} the navgroup
17267 get: function(navId) {
17268 if (typeof(this.groups[navId]) == 'undefined') {
17269 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17271 return this.groups[navId] ;
17286 * @class Roo.bootstrap.TabPanel
17287 * @extends Roo.bootstrap.Component
17288 * Bootstrap TabPanel class
17289 * @cfg {Boolean} active panel active
17290 * @cfg {String} html panel content
17291 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17292 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17293 * @cfg {String} href click to link..
17297 * Create a new TabPanel
17298 * @param {Object} config The config object
17301 Roo.bootstrap.TabPanel = function(config){
17302 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17306 * Fires when the active status changes
17307 * @param {Roo.bootstrap.TabPanel} this
17308 * @param {Boolean} state the new state
17313 * @event beforedeactivate
17314 * Fires before a tab is de-activated - can be used to do validation on a form.
17315 * @param {Roo.bootstrap.TabPanel} this
17316 * @return {Boolean} false if there is an error
17319 'beforedeactivate': true
17322 this.tabId = this.tabId || Roo.id();
17326 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
17334 getAutoCreate : function(){
17337 // item is needed for carousel - not sure if it has any effect otherwise
17338 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17339 html: this.html || ''
17343 cfg.cls += ' active';
17347 cfg.tabId = this.tabId;
17354 initEvents: function()
17356 var p = this.parent();
17358 this.navId = this.navId || p.navId;
17360 if (typeof(this.navId) != 'undefined') {
17361 // not really needed.. but just in case.. parent should be a NavGroup.
17362 var tg = Roo.bootstrap.TabGroup.get(this.navId);
17366 var i = tg.tabs.length - 1;
17368 if(this.active && tg.bullets > 0 && i < tg.bullets){
17369 tg.setActiveBullet(i);
17373 this.el.on('click', this.onClick, this);
17376 this.el.on("touchstart", this.onTouchStart, this);
17377 this.el.on("touchmove", this.onTouchMove, this);
17378 this.el.on("touchend", this.onTouchEnd, this);
17383 onRender : function(ct, position)
17385 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17388 setActive : function(state)
17390 Roo.log("panel - set active " + this.tabId + "=" + state);
17392 this.active = state;
17394 this.el.removeClass('active');
17396 } else if (!this.el.hasClass('active')) {
17397 this.el.addClass('active');
17400 this.fireEvent('changed', this, state);
17403 onClick : function(e)
17405 e.preventDefault();
17407 if(!this.href.length){
17411 window.location.href = this.href;
17420 onTouchStart : function(e)
17422 this.swiping = false;
17424 this.startX = e.browserEvent.touches[0].clientX;
17425 this.startY = e.browserEvent.touches[0].clientY;
17428 onTouchMove : function(e)
17430 this.swiping = true;
17432 this.endX = e.browserEvent.touches[0].clientX;
17433 this.endY = e.browserEvent.touches[0].clientY;
17436 onTouchEnd : function(e)
17443 var tabGroup = this.parent();
17445 if(this.endX > this.startX){ // swiping right
17446 tabGroup.showPanelPrev();
17450 if(this.startX > this.endX){ // swiping left
17451 tabGroup.showPanelNext();
17470 * @class Roo.bootstrap.DateField
17471 * @extends Roo.bootstrap.Input
17472 * Bootstrap DateField class
17473 * @cfg {Number} weekStart default 0
17474 * @cfg {String} viewMode default empty, (months|years)
17475 * @cfg {String} minViewMode default empty, (months|years)
17476 * @cfg {Number} startDate default -Infinity
17477 * @cfg {Number} endDate default Infinity
17478 * @cfg {Boolean} todayHighlight default false
17479 * @cfg {Boolean} todayBtn default false
17480 * @cfg {Boolean} calendarWeeks default false
17481 * @cfg {Object} daysOfWeekDisabled default empty
17482 * @cfg {Boolean} singleMode default false (true | false)
17484 * @cfg {Boolean} keyboardNavigation default true
17485 * @cfg {String} language default en
17488 * Create a new DateField
17489 * @param {Object} config The config object
17492 Roo.bootstrap.DateField = function(config){
17493 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17497 * Fires when this field show.
17498 * @param {Roo.bootstrap.DateField} this
17499 * @param {Mixed} date The date value
17504 * Fires when this field hide.
17505 * @param {Roo.bootstrap.DateField} this
17506 * @param {Mixed} date The date value
17511 * Fires when select a date.
17512 * @param {Roo.bootstrap.DateField} this
17513 * @param {Mixed} date The date value
17517 * @event beforeselect
17518 * Fires when before select a date.
17519 * @param {Roo.bootstrap.DateField} this
17520 * @param {Mixed} date The date value
17522 beforeselect : true
17526 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
17529 * @cfg {String} format
17530 * The default date format string which can be overriden for localization support. The format must be
17531 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17535 * @cfg {String} altFormats
17536 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17537 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17539 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17547 todayHighlight : false,
17553 keyboardNavigation: true,
17555 calendarWeeks: false,
17557 startDate: -Infinity,
17561 daysOfWeekDisabled: [],
17565 singleMode : false,
17567 UTCDate: function()
17569 return new Date(Date.UTC.apply(Date, arguments));
17572 UTCToday: function()
17574 var today = new Date();
17575 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17578 getDate: function() {
17579 var d = this.getUTCDate();
17580 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17583 getUTCDate: function() {
17587 setDate: function(d) {
17588 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17591 setUTCDate: function(d) {
17593 this.setValue(this.formatDate(this.date));
17596 onRender: function(ct, position)
17599 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17601 this.language = this.language || 'en';
17602 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17603 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17605 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17606 this.format = this.format || 'm/d/y';
17607 this.isInline = false;
17608 this.isInput = true;
17609 this.component = this.el.select('.add-on', true).first() || false;
17610 this.component = (this.component && this.component.length === 0) ? false : this.component;
17611 this.hasInput = this.component && this.inputEl().length;
17613 if (typeof(this.minViewMode === 'string')) {
17614 switch (this.minViewMode) {
17616 this.minViewMode = 1;
17619 this.minViewMode = 2;
17622 this.minViewMode = 0;
17627 if (typeof(this.viewMode === 'string')) {
17628 switch (this.viewMode) {
17641 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17643 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17645 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17647 this.picker().on('mousedown', this.onMousedown, this);
17648 this.picker().on('click', this.onClick, this);
17650 this.picker().addClass('datepicker-dropdown');
17652 this.startViewMode = this.viewMode;
17654 if(this.singleMode){
17655 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17656 v.setVisibilityMode(Roo.Element.DISPLAY);
17660 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17661 v.setStyle('width', '189px');
17665 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17666 if(!this.calendarWeeks){
17671 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17672 v.attr('colspan', function(i, val){
17673 return parseInt(val) + 1;
17678 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17680 this.setStartDate(this.startDate);
17681 this.setEndDate(this.endDate);
17683 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17690 if(this.isInline) {
17695 picker : function()
17697 return this.pickerEl;
17698 // return this.el.select('.datepicker', true).first();
17701 fillDow: function()
17703 var dowCnt = this.weekStart;
17712 if(this.calendarWeeks){
17720 while (dowCnt < this.weekStart + 7) {
17724 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17728 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17731 fillMonths: function()
17734 var months = this.picker().select('>.datepicker-months td', true).first();
17736 months.dom.innerHTML = '';
17742 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17745 months.createChild(month);
17752 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;
17754 if (this.date < this.startDate) {
17755 this.viewDate = new Date(this.startDate);
17756 } else if (this.date > this.endDate) {
17757 this.viewDate = new Date(this.endDate);
17759 this.viewDate = new Date(this.date);
17767 var d = new Date(this.viewDate),
17768 year = d.getUTCFullYear(),
17769 month = d.getUTCMonth(),
17770 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17771 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17772 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17773 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17774 currentDate = this.date && this.date.valueOf(),
17775 today = this.UTCToday();
17777 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17779 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17781 // this.picker.select('>tfoot th.today').
17782 // .text(dates[this.language].today)
17783 // .toggle(this.todayBtn !== false);
17785 this.updateNavArrows();
17788 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17790 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17792 prevMonth.setUTCDate(day);
17794 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17796 var nextMonth = new Date(prevMonth);
17798 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17800 nextMonth = nextMonth.valueOf();
17802 var fillMonths = false;
17804 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17806 while(prevMonth.valueOf() < nextMonth) {
17809 if (prevMonth.getUTCDay() === this.weekStart) {
17811 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17819 if(this.calendarWeeks){
17820 // ISO 8601: First week contains first thursday.
17821 // ISO also states week starts on Monday, but we can be more abstract here.
17823 // Start of current week: based on weekstart/current date
17824 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17825 // Thursday of this week
17826 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17827 // First Thursday of year, year from thursday
17828 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17829 // Calendar week: ms between thursdays, div ms per day, div 7 days
17830 calWeek = (th - yth) / 864e5 / 7 + 1;
17832 fillMonths.cn.push({
17840 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17842 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17845 if (this.todayHighlight &&
17846 prevMonth.getUTCFullYear() == today.getFullYear() &&
17847 prevMonth.getUTCMonth() == today.getMonth() &&
17848 prevMonth.getUTCDate() == today.getDate()) {
17849 clsName += ' today';
17852 if (currentDate && prevMonth.valueOf() === currentDate) {
17853 clsName += ' active';
17856 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17857 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17858 clsName += ' disabled';
17861 fillMonths.cn.push({
17863 cls: 'day ' + clsName,
17864 html: prevMonth.getDate()
17867 prevMonth.setDate(prevMonth.getDate()+1);
17870 var currentYear = this.date && this.date.getUTCFullYear();
17871 var currentMonth = this.date && this.date.getUTCMonth();
17873 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17875 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17876 v.removeClass('active');
17878 if(currentYear === year && k === currentMonth){
17879 v.addClass('active');
17882 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17883 v.addClass('disabled');
17889 year = parseInt(year/10, 10) * 10;
17891 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17893 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17896 for (var i = -1; i < 11; i++) {
17897 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17899 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17907 showMode: function(dir)
17910 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17913 Roo.each(this.picker().select('>div',true).elements, function(v){
17914 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17917 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17922 if(this.isInline) {
17926 this.picker().removeClass(['bottom', 'top']);
17928 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17930 * place to the top of element!
17934 this.picker().addClass('top');
17935 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17940 this.picker().addClass('bottom');
17942 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17945 parseDate : function(value)
17947 if(!value || value instanceof Date){
17950 var v = Date.parseDate(value, this.format);
17951 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17952 v = Date.parseDate(value, 'Y-m-d');
17954 if(!v && this.altFormats){
17955 if(!this.altFormatsArray){
17956 this.altFormatsArray = this.altFormats.split("|");
17958 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17959 v = Date.parseDate(value, this.altFormatsArray[i]);
17965 formatDate : function(date, fmt)
17967 return (!date || !(date instanceof Date)) ?
17968 date : date.dateFormat(fmt || this.format);
17971 onFocus : function()
17973 Roo.bootstrap.DateField.superclass.onFocus.call(this);
17977 onBlur : function()
17979 Roo.bootstrap.DateField.superclass.onBlur.call(this);
17981 var d = this.inputEl().getValue();
17990 this.picker().show();
17994 this.fireEvent('show', this, this.date);
17999 if(this.isInline) {
18002 this.picker().hide();
18003 this.viewMode = this.startViewMode;
18006 this.fireEvent('hide', this, this.date);
18010 onMousedown: function(e)
18012 e.stopPropagation();
18013 e.preventDefault();
18018 Roo.bootstrap.DateField.superclass.keyup.call(this);
18022 setValue: function(v)
18024 if(this.fireEvent('beforeselect', this, v) !== false){
18025 var d = new Date(this.parseDate(v) ).clearTime();
18027 if(isNaN(d.getTime())){
18028 this.date = this.viewDate = '';
18029 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18033 v = this.formatDate(d);
18035 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18037 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18041 this.fireEvent('select', this, this.date);
18045 getValue: function()
18047 return this.formatDate(this.date);
18050 fireKey: function(e)
18052 if (!this.picker().isVisible()){
18053 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18059 var dateChanged = false,
18061 newDate, newViewDate;
18066 e.preventDefault();
18070 if (!this.keyboardNavigation) {
18073 dir = e.keyCode == 37 ? -1 : 1;
18076 newDate = this.moveYear(this.date, dir);
18077 newViewDate = this.moveYear(this.viewDate, dir);
18078 } else if (e.shiftKey){
18079 newDate = this.moveMonth(this.date, dir);
18080 newViewDate = this.moveMonth(this.viewDate, dir);
18082 newDate = new Date(this.date);
18083 newDate.setUTCDate(this.date.getUTCDate() + dir);
18084 newViewDate = new Date(this.viewDate);
18085 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18087 if (this.dateWithinRange(newDate)){
18088 this.date = newDate;
18089 this.viewDate = newViewDate;
18090 this.setValue(this.formatDate(this.date));
18092 e.preventDefault();
18093 dateChanged = true;
18098 if (!this.keyboardNavigation) {
18101 dir = e.keyCode == 38 ? -1 : 1;
18103 newDate = this.moveYear(this.date, dir);
18104 newViewDate = this.moveYear(this.viewDate, dir);
18105 } else if (e.shiftKey){
18106 newDate = this.moveMonth(this.date, dir);
18107 newViewDate = this.moveMonth(this.viewDate, dir);
18109 newDate = new Date(this.date);
18110 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18111 newViewDate = new Date(this.viewDate);
18112 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18114 if (this.dateWithinRange(newDate)){
18115 this.date = newDate;
18116 this.viewDate = newViewDate;
18117 this.setValue(this.formatDate(this.date));
18119 e.preventDefault();
18120 dateChanged = true;
18124 this.setValue(this.formatDate(this.date));
18126 e.preventDefault();
18129 this.setValue(this.formatDate(this.date));
18143 onClick: function(e)
18145 e.stopPropagation();
18146 e.preventDefault();
18148 var target = e.getTarget();
18150 if(target.nodeName.toLowerCase() === 'i'){
18151 target = Roo.get(target).dom.parentNode;
18154 var nodeName = target.nodeName;
18155 var className = target.className;
18156 var html = target.innerHTML;
18157 //Roo.log(nodeName);
18159 switch(nodeName.toLowerCase()) {
18161 switch(className) {
18167 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18168 switch(this.viewMode){
18170 this.viewDate = this.moveMonth(this.viewDate, dir);
18174 this.viewDate = this.moveYear(this.viewDate, dir);
18180 var date = new Date();
18181 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18183 this.setValue(this.formatDate(this.date));
18190 if (className.indexOf('disabled') < 0) {
18191 this.viewDate.setUTCDate(1);
18192 if (className.indexOf('month') > -1) {
18193 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18195 var year = parseInt(html, 10) || 0;
18196 this.viewDate.setUTCFullYear(year);
18200 if(this.singleMode){
18201 this.setValue(this.formatDate(this.viewDate));
18212 //Roo.log(className);
18213 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18214 var day = parseInt(html, 10) || 1;
18215 var year = this.viewDate.getUTCFullYear(),
18216 month = this.viewDate.getUTCMonth();
18218 if (className.indexOf('old') > -1) {
18225 } else if (className.indexOf('new') > -1) {
18233 //Roo.log([year,month,day]);
18234 this.date = this.UTCDate(year, month, day,0,0,0,0);
18235 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18237 //Roo.log(this.formatDate(this.date));
18238 this.setValue(this.formatDate(this.date));
18245 setStartDate: function(startDate)
18247 this.startDate = startDate || -Infinity;
18248 if (this.startDate !== -Infinity) {
18249 this.startDate = this.parseDate(this.startDate);
18252 this.updateNavArrows();
18255 setEndDate: function(endDate)
18257 this.endDate = endDate || Infinity;
18258 if (this.endDate !== Infinity) {
18259 this.endDate = this.parseDate(this.endDate);
18262 this.updateNavArrows();
18265 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18267 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18268 if (typeof(this.daysOfWeekDisabled) !== 'object') {
18269 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18271 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18272 return parseInt(d, 10);
18275 this.updateNavArrows();
18278 updateNavArrows: function()
18280 if(this.singleMode){
18284 var d = new Date(this.viewDate),
18285 year = d.getUTCFullYear(),
18286 month = d.getUTCMonth();
18288 Roo.each(this.picker().select('.prev', true).elements, function(v){
18290 switch (this.viewMode) {
18293 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18299 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18306 Roo.each(this.picker().select('.next', true).elements, function(v){
18308 switch (this.viewMode) {
18311 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18317 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18325 moveMonth: function(date, dir)
18330 var new_date = new Date(date.valueOf()),
18331 day = new_date.getUTCDate(),
18332 month = new_date.getUTCMonth(),
18333 mag = Math.abs(dir),
18335 dir = dir > 0 ? 1 : -1;
18338 // If going back one month, make sure month is not current month
18339 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18341 return new_date.getUTCMonth() == month;
18343 // If going forward one month, make sure month is as expected
18344 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18346 return new_date.getUTCMonth() != new_month;
18348 new_month = month + dir;
18349 new_date.setUTCMonth(new_month);
18350 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18351 if (new_month < 0 || new_month > 11) {
18352 new_month = (new_month + 12) % 12;
18355 // For magnitudes >1, move one month at a time...
18356 for (var i=0; i<mag; i++) {
18357 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18358 new_date = this.moveMonth(new_date, dir);
18360 // ...then reset the day, keeping it in the new month
18361 new_month = new_date.getUTCMonth();
18362 new_date.setUTCDate(day);
18364 return new_month != new_date.getUTCMonth();
18367 // Common date-resetting loop -- if date is beyond end of month, make it
18370 new_date.setUTCDate(--day);
18371 new_date.setUTCMonth(new_month);
18376 moveYear: function(date, dir)
18378 return this.moveMonth(date, dir*12);
18381 dateWithinRange: function(date)
18383 return date >= this.startDate && date <= this.endDate;
18389 this.picker().remove();
18392 validateValue : function(value)
18394 if(value.length < 1) {
18395 if(this.allowBlank){
18401 if(value.length < this.minLength){
18404 if(value.length > this.maxLength){
18408 var vt = Roo.form.VTypes;
18409 if(!vt[this.vtype](value, this)){
18413 if(typeof this.validator == "function"){
18414 var msg = this.validator(value);
18420 if(this.regex && !this.regex.test(value)){
18424 if(typeof(this.parseDate(value)) == 'undefined'){
18428 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18432 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18442 Roo.apply(Roo.bootstrap.DateField, {
18453 html: '<i class="fa fa-arrow-left"/>'
18463 html: '<i class="fa fa-arrow-right"/>'
18505 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18506 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18507 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18508 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18509 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18522 navFnc: 'FullYear',
18527 navFnc: 'FullYear',
18532 Roo.apply(Roo.bootstrap.DateField, {
18536 cls: 'datepicker dropdown-menu roo-dynamic',
18540 cls: 'datepicker-days',
18544 cls: 'table-condensed',
18546 Roo.bootstrap.DateField.head,
18550 Roo.bootstrap.DateField.footer
18557 cls: 'datepicker-months',
18561 cls: 'table-condensed',
18563 Roo.bootstrap.DateField.head,
18564 Roo.bootstrap.DateField.content,
18565 Roo.bootstrap.DateField.footer
18572 cls: 'datepicker-years',
18576 cls: 'table-condensed',
18578 Roo.bootstrap.DateField.head,
18579 Roo.bootstrap.DateField.content,
18580 Roo.bootstrap.DateField.footer
18599 * @class Roo.bootstrap.TimeField
18600 * @extends Roo.bootstrap.Input
18601 * Bootstrap DateField class
18605 * Create a new TimeField
18606 * @param {Object} config The config object
18609 Roo.bootstrap.TimeField = function(config){
18610 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18614 * Fires when this field show.
18615 * @param {Roo.bootstrap.DateField} thisthis
18616 * @param {Mixed} date The date value
18621 * Fires when this field hide.
18622 * @param {Roo.bootstrap.DateField} this
18623 * @param {Mixed} date The date value
18628 * Fires when select a date.
18629 * @param {Roo.bootstrap.DateField} this
18630 * @param {Mixed} date The date value
18636 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
18639 * @cfg {String} format
18640 * The default time format string which can be overriden for localization support. The format must be
18641 * valid according to {@link Date#parseDate} (defaults to 'H:i').
18645 onRender: function(ct, position)
18648 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18650 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18652 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18654 this.pop = this.picker().select('>.datepicker-time',true).first();
18655 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18657 this.picker().on('mousedown', this.onMousedown, this);
18658 this.picker().on('click', this.onClick, this);
18660 this.picker().addClass('datepicker-dropdown');
18665 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18666 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18667 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18668 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18669 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18670 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18674 fireKey: function(e){
18675 if (!this.picker().isVisible()){
18676 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18682 e.preventDefault();
18690 this.onTogglePeriod();
18693 this.onIncrementMinutes();
18696 this.onDecrementMinutes();
18705 onClick: function(e) {
18706 e.stopPropagation();
18707 e.preventDefault();
18710 picker : function()
18712 return this.el.select('.datepicker', true).first();
18715 fillTime: function()
18717 var time = this.pop.select('tbody', true).first();
18719 time.dom.innerHTML = '';
18734 cls: 'hours-up glyphicon glyphicon-chevron-up'
18754 cls: 'minutes-up glyphicon glyphicon-chevron-up'
18775 cls: 'timepicker-hour',
18790 cls: 'timepicker-minute',
18805 cls: 'btn btn-primary period',
18827 cls: 'hours-down glyphicon glyphicon-chevron-down'
18847 cls: 'minutes-down glyphicon glyphicon-chevron-down'
18865 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18872 var hours = this.time.getHours();
18873 var minutes = this.time.getMinutes();
18886 hours = hours - 12;
18890 hours = '0' + hours;
18894 minutes = '0' + minutes;
18897 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18898 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18899 this.pop.select('button', true).first().dom.innerHTML = period;
18905 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18907 var cls = ['bottom'];
18909 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18916 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18921 this.picker().addClass(cls.join('-'));
18925 Roo.each(cls, function(c){
18927 _this.picker().setTop(_this.inputEl().getHeight());
18931 _this.picker().setTop(0 - _this.picker().getHeight());
18936 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18940 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18947 onFocus : function()
18949 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18953 onBlur : function()
18955 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18961 this.picker().show();
18966 this.fireEvent('show', this, this.date);
18971 this.picker().hide();
18974 this.fireEvent('hide', this, this.date);
18977 setTime : function()
18980 this.setValue(this.time.format(this.format));
18982 this.fireEvent('select', this, this.date);
18987 onMousedown: function(e){
18988 e.stopPropagation();
18989 e.preventDefault();
18992 onIncrementHours: function()
18994 Roo.log('onIncrementHours');
18995 this.time = this.time.add(Date.HOUR, 1);
19000 onDecrementHours: function()
19002 Roo.log('onDecrementHours');
19003 this.time = this.time.add(Date.HOUR, -1);
19007 onIncrementMinutes: function()
19009 Roo.log('onIncrementMinutes');
19010 this.time = this.time.add(Date.MINUTE, 1);
19014 onDecrementMinutes: function()
19016 Roo.log('onDecrementMinutes');
19017 this.time = this.time.add(Date.MINUTE, -1);
19021 onTogglePeriod: function()
19023 Roo.log('onTogglePeriod');
19024 this.time = this.time.add(Date.HOUR, 12);
19031 Roo.apply(Roo.bootstrap.TimeField, {
19061 cls: 'btn btn-info ok',
19073 Roo.apply(Roo.bootstrap.TimeField, {
19077 cls: 'datepicker dropdown-menu',
19081 cls: 'datepicker-time',
19085 cls: 'table-condensed',
19087 Roo.bootstrap.TimeField.content,
19088 Roo.bootstrap.TimeField.footer
19107 * @class Roo.bootstrap.MonthField
19108 * @extends Roo.bootstrap.Input
19109 * Bootstrap MonthField class
19111 * @cfg {String} language default en
19114 * Create a new MonthField
19115 * @param {Object} config The config object
19118 Roo.bootstrap.MonthField = function(config){
19119 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19124 * Fires when this field show.
19125 * @param {Roo.bootstrap.MonthField} this
19126 * @param {Mixed} date The date value
19131 * Fires when this field hide.
19132 * @param {Roo.bootstrap.MonthField} this
19133 * @param {Mixed} date The date value
19138 * Fires when select a date.
19139 * @param {Roo.bootstrap.MonthField} this
19140 * @param {String} oldvalue The old value
19141 * @param {String} newvalue The new value
19147 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
19149 onRender: function(ct, position)
19152 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19154 this.language = this.language || 'en';
19155 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19156 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19158 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19159 this.isInline = false;
19160 this.isInput = true;
19161 this.component = this.el.select('.add-on', true).first() || false;
19162 this.component = (this.component && this.component.length === 0) ? false : this.component;
19163 this.hasInput = this.component && this.inputEL().length;
19165 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19167 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19169 this.picker().on('mousedown', this.onMousedown, this);
19170 this.picker().on('click', this.onClick, this);
19172 this.picker().addClass('datepicker-dropdown');
19174 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19175 v.setStyle('width', '189px');
19182 if(this.isInline) {
19188 setValue: function(v, suppressEvent)
19190 var o = this.getValue();
19192 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19196 if(suppressEvent !== true){
19197 this.fireEvent('select', this, o, v);
19202 getValue: function()
19207 onClick: function(e)
19209 e.stopPropagation();
19210 e.preventDefault();
19212 var target = e.getTarget();
19214 if(target.nodeName.toLowerCase() === 'i'){
19215 target = Roo.get(target).dom.parentNode;
19218 var nodeName = target.nodeName;
19219 var className = target.className;
19220 var html = target.innerHTML;
19222 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19226 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19228 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19234 picker : function()
19236 return this.pickerEl;
19239 fillMonths: function()
19242 var months = this.picker().select('>.datepicker-months td', true).first();
19244 months.dom.innerHTML = '';
19250 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19253 months.createChild(month);
19262 if(typeof(this.vIndex) == 'undefined' && this.value.length){
19263 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19266 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19267 e.removeClass('active');
19269 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19270 e.addClass('active');
19277 if(this.isInline) {
19281 this.picker().removeClass(['bottom', 'top']);
19283 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19285 * place to the top of element!
19289 this.picker().addClass('top');
19290 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19295 this.picker().addClass('bottom');
19297 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19300 onFocus : function()
19302 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19306 onBlur : function()
19308 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19310 var d = this.inputEl().getValue();
19319 this.picker().show();
19320 this.picker().select('>.datepicker-months', true).first().show();
19324 this.fireEvent('show', this, this.date);
19329 if(this.isInline) {
19332 this.picker().hide();
19333 this.fireEvent('hide', this, this.date);
19337 onMousedown: function(e)
19339 e.stopPropagation();
19340 e.preventDefault();
19345 Roo.bootstrap.MonthField.superclass.keyup.call(this);
19349 fireKey: function(e)
19351 if (!this.picker().isVisible()){
19352 if (e.keyCode == 27) {// allow escape to hide and re-show picker
19363 e.preventDefault();
19367 dir = e.keyCode == 37 ? -1 : 1;
19369 this.vIndex = this.vIndex + dir;
19371 if(this.vIndex < 0){
19375 if(this.vIndex > 11){
19379 if(isNaN(this.vIndex)){
19383 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19389 dir = e.keyCode == 38 ? -1 : 1;
19391 this.vIndex = this.vIndex + dir * 4;
19393 if(this.vIndex < 0){
19397 if(this.vIndex > 11){
19401 if(isNaN(this.vIndex)){
19405 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19410 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19411 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19415 e.preventDefault();
19418 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19419 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19435 this.picker().remove();
19440 Roo.apply(Roo.bootstrap.MonthField, {
19459 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19460 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19465 Roo.apply(Roo.bootstrap.MonthField, {
19469 cls: 'datepicker dropdown-menu roo-dynamic',
19473 cls: 'datepicker-months',
19477 cls: 'table-condensed',
19479 Roo.bootstrap.DateField.content
19499 * @class Roo.bootstrap.CheckBox
19500 * @extends Roo.bootstrap.Input
19501 * Bootstrap CheckBox class
19503 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19504 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19505 * @cfg {String} boxLabel The text that appears beside the checkbox
19506 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19507 * @cfg {Boolean} checked initnal the element
19508 * @cfg {Boolean} inline inline the element (default false)
19509 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19512 * Create a new CheckBox
19513 * @param {Object} config The config object
19516 Roo.bootstrap.CheckBox = function(config){
19517 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19522 * Fires when the element is checked or unchecked.
19523 * @param {Roo.bootstrap.CheckBox} this This input
19524 * @param {Boolean} checked The new checked value
19531 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
19533 inputType: 'checkbox',
19541 getAutoCreate : function()
19543 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19549 cfg.cls = 'form-group ' + this.inputType; //input-group
19552 cfg.cls += ' ' + this.inputType + '-inline';
19558 type : this.inputType,
19559 value : this.inputValue,
19560 cls : 'roo-' + this.inputType, //'form-box',
19561 placeholder : this.placeholder || ''
19565 if(this.inputType != 'radio'){
19569 cls : 'roo-hidden-value',
19570 value : this.checked ? this.valueOff : this.inputValue
19575 if (this.weight) { // Validity check?
19576 cfg.cls += " " + this.inputType + "-" + this.weight;
19579 if (this.disabled) {
19580 input.disabled=true;
19584 input.checked = this.checked;
19591 input.name = this.name;
19593 if(this.inputType != 'radio'){
19594 hidden.name = this.name;
19595 input.name = '_hidden_' + this.name;
19600 input.cls += ' input-' + this.size;
19605 ['xs','sm','md','lg'].map(function(size){
19606 if (settings[size]) {
19607 cfg.cls += ' col-' + size + '-' + settings[size];
19611 var inputblock = input;
19613 if (this.before || this.after) {
19616 cls : 'input-group',
19621 inputblock.cn.push({
19623 cls : 'input-group-addon',
19628 inputblock.cn.push(input);
19630 if(this.inputType != 'radio'){
19631 inputblock.cn.push(hidden);
19635 inputblock.cn.push({
19637 cls : 'input-group-addon',
19644 if (align ==='left' && this.fieldLabel.length) {
19645 // Roo.log("left and has label");
19651 cls : 'control-label col-md-' + this.labelWidth,
19652 html : this.fieldLabel
19656 cls : "col-md-" + (12 - this.labelWidth),
19663 } else if ( this.fieldLabel.length) {
19664 // Roo.log(" label");
19668 tag: this.boxLabel ? 'span' : 'label',
19670 cls: 'control-label box-input-label',
19671 //cls : 'input-group-addon',
19672 html : this.fieldLabel
19682 // Roo.log(" no label && no align");
19683 cfg.cn = [ inputblock ] ;
19689 var boxLabelCfg = {
19691 //'for': id, // box label is handled by onclick - so no for...
19693 html: this.boxLabel
19697 boxLabelCfg.tooltip = this.tooltip;
19700 cfg.cn.push(boxLabelCfg);
19703 if(this.inputType != 'radio'){
19704 cfg.cn.push(hidden);
19712 * return the real input element.
19714 inputEl: function ()
19716 return this.el.select('input.roo-' + this.inputType,true).first();
19718 hiddenEl: function ()
19720 return this.el.select('input.roo-hidden-value',true).first();
19723 labelEl: function()
19725 return this.el.select('label.control-label',true).first();
19727 /* depricated... */
19731 return this.labelEl();
19734 boxLabelEl: function()
19736 return this.el.select('label.box-label',true).first();
19739 initEvents : function()
19741 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19743 this.inputEl().on('click', this.onClick, this);
19745 if (this.boxLabel) {
19746 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
19749 this.startValue = this.getValue();
19752 Roo.bootstrap.CheckBox.register(this);
19756 onClick : function()
19758 this.setChecked(!this.checked);
19761 setChecked : function(state,suppressEvent)
19763 this.startValue = this.getValue();
19765 if(this.inputType == 'radio'){
19767 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19768 e.dom.checked = false;
19771 this.inputEl().dom.checked = true;
19773 this.inputEl().dom.value = this.inputValue;
19775 if(suppressEvent !== true){
19776 this.fireEvent('check', this, true);
19784 this.checked = state;
19786 this.inputEl().dom.checked = state;
19789 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
19791 if(suppressEvent !== true){
19792 this.fireEvent('check', this, state);
19798 getValue : function()
19800 if(this.inputType == 'radio'){
19801 return this.getGroupValue();
19804 return this.hiddenEl().dom.value;
19808 getGroupValue : function()
19810 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19814 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19817 setValue : function(v,suppressEvent)
19819 if(this.inputType == 'radio'){
19820 this.setGroupValue(v, suppressEvent);
19824 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19829 setGroupValue : function(v, suppressEvent)
19831 this.startValue = this.getValue();
19833 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19834 e.dom.checked = false;
19836 if(e.dom.value == v){
19837 e.dom.checked = true;
19841 if(suppressEvent !== true){
19842 this.fireEvent('check', this, true);
19850 validate : function()
19854 (this.inputType == 'radio' && this.validateRadio()) ||
19855 (this.inputType == 'checkbox' && this.validateCheckbox())
19861 this.markInvalid();
19865 validateRadio : function()
19867 if(this.allowBlank){
19873 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19874 if(!e.dom.checked){
19886 validateCheckbox : function()
19889 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19892 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19900 for(var i in group){
19905 r = (group[i].getValue() == group[i].inputValue) ? true : false;
19912 * Mark this field as valid
19914 markValid : function()
19916 if(this.allowBlank){
19922 this.fireEvent('valid', this);
19924 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19927 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19934 if(this.inputType == 'radio'){
19935 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19936 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19937 e.findParent('.form-group', false, true).addClass(_this.validClass);
19944 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19945 this.el.findParent('.form-group', false, true).addClass(this.validClass);
19949 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19955 for(var i in group){
19956 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19957 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19962 * Mark this field as invalid
19963 * @param {String} msg The validation message
19965 markInvalid : function(msg)
19967 if(this.allowBlank){
19973 this.fireEvent('invalid', this, msg);
19975 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19978 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19982 label.markInvalid();
19985 if(this.inputType == 'radio'){
19986 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19987 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19988 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19995 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19996 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20000 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20006 for(var i in group){
20007 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20008 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20013 disable : function()
20015 if(this.inputType != 'radio'){
20016 Roo.bootstrap.CheckBox.superclass.disable.call(this);
20023 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20024 _this.getActionEl().addClass(this.disabledClass);
20025 e.dom.disabled = true;
20029 this.disabled = true;
20030 this.fireEvent("disable", this);
20034 enable : function()
20036 if(this.inputType != 'radio'){
20037 Roo.bootstrap.CheckBox.superclass.enable.call(this);
20044 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20045 _this.getActionEl().removeClass(this.disabledClass);
20046 e.dom.disabled = false;
20050 this.disabled = false;
20051 this.fireEvent("enable", this);
20057 Roo.apply(Roo.bootstrap.CheckBox, {
20062 * register a CheckBox Group
20063 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20065 register : function(checkbox)
20067 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20068 this.groups[checkbox.groupId] = {};
20071 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20075 this.groups[checkbox.groupId][checkbox.name] = checkbox;
20079 * fetch a CheckBox Group based on the group ID
20080 * @param {string} the group ID
20081 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20083 get: function(groupId) {
20084 if (typeof(this.groups[groupId]) == 'undefined') {
20088 return this.groups[groupId] ;
20100 *<div class="radio">
20102 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
20103 Option one is this and that—be sure to include why it's great
20110 *<label class="radio-inline">fieldLabel</label>
20111 *<label class="radio-inline">
20112 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
20120 * @class Roo.bootstrap.Radio
20121 * @extends Roo.bootstrap.CheckBox
20122 * Bootstrap Radio class
20125 * Create a new Radio
20126 * @param {Object} config The config object
20129 Roo.bootstrap.Radio = function(config){
20130 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20134 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
20136 inputType: 'radio',
20140 getAutoCreate : function()
20142 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20143 align = align || 'left'; // default...
20150 tag : this.inline ? 'span' : 'div',
20151 cls : 'form-group',
20155 var inline = this.inline ? ' radio-inline' : '';
20159 // does not need for, as we wrap the input with it..
20161 cls : 'control-label box-label' + inline,
20164 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
20168 //cls : 'control-label' + inline,
20169 html : this.fieldLabel,
20170 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
20176 type : this.inputType,
20177 //value : (!this.checked) ? this.valueOff : this.inputValue,
20178 value : this.inputValue,
20180 placeholder : this.placeholder || '' // ?? needed????
20183 if (this.weight) { // Validity check?
20184 input.cls += " radio-" + this.weight;
20186 if (this.disabled) {
20187 input.disabled=true;
20191 input.checked = this.checked;
20195 input.name = this.name;
20199 input.cls += ' input-' + this.size;
20202 //?? can span's inline have a width??
20205 ['xs','sm','md','lg'].map(function(size){
20206 if (settings[size]) {
20207 cfg.cls += ' col-' + size + '-' + settings[size];
20211 var inputblock = input;
20213 if (this.before || this.after) {
20216 cls : 'input-group',
20221 inputblock.cn.push({
20223 cls : 'input-group-addon',
20227 inputblock.cn.push(input);
20229 inputblock.cn.push({
20231 cls : 'input-group-addon',
20239 if (this.fieldLabel && this.fieldLabel.length) {
20240 cfg.cn.push(fieldLabel);
20243 // normal bootstrap puts the input inside the label.
20244 // however with our styled version - it has to go after the input.
20246 //lbl.cn.push(inputblock);
20250 cls: 'radio' + inline,
20257 cfg.cn.push( lblwrap);
20262 html: this.boxLabel
20271 initEvents : function()
20273 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20275 this.inputEl().on('click', this.onClick, this);
20276 if (this.boxLabel) {
20277 //Roo.log('find label');
20278 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
20283 inputEl: function ()
20285 return this.el.select('input.roo-radio',true).first();
20287 onClick : function()
20290 this.setChecked(true);
20293 setChecked : function(state,suppressEvent)
20296 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20297 v.dom.checked = false;
20300 Roo.log(this.inputEl().dom);
20301 this.checked = state;
20302 this.inputEl().dom.checked = state;
20304 if(suppressEvent !== true){
20305 this.fireEvent('check', this, state);
20308 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
20312 getGroupValue : function()
20315 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20316 if(v.dom.checked == true){
20317 value = v.dom.value;
20325 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
20326 * @return {Mixed} value The field value
20328 getValue : function(){
20329 return this.getGroupValue();
20333 //<script type="text/javascript">
20336 * Based Ext JS Library 1.1.1
20337 * Copyright(c) 2006-2007, Ext JS, LLC.
20343 * @class Roo.HtmlEditorCore
20344 * @extends Roo.Component
20345 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20347 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20350 Roo.HtmlEditorCore = function(config){
20353 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20358 * @event initialize
20359 * Fires when the editor is fully initialized (including the iframe)
20360 * @param {Roo.HtmlEditorCore} this
20365 * Fires when the editor is first receives the focus. Any insertion must wait
20366 * until after this event.
20367 * @param {Roo.HtmlEditorCore} this
20371 * @event beforesync
20372 * Fires before the textarea is updated with content from the editor iframe. Return false
20373 * to cancel the sync.
20374 * @param {Roo.HtmlEditorCore} this
20375 * @param {String} html
20379 * @event beforepush
20380 * Fires before the iframe editor is updated with content from the textarea. Return false
20381 * to cancel the push.
20382 * @param {Roo.HtmlEditorCore} this
20383 * @param {String} html
20388 * Fires when the textarea is updated with content from the editor iframe.
20389 * @param {Roo.HtmlEditorCore} this
20390 * @param {String} html
20395 * Fires when the iframe editor is updated with content from the textarea.
20396 * @param {Roo.HtmlEditorCore} this
20397 * @param {String} html
20402 * @event editorevent
20403 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20404 * @param {Roo.HtmlEditorCore} this
20410 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20412 // defaults : white / black...
20413 this.applyBlacklists();
20420 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
20424 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
20430 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20435 * @cfg {Number} height (in pixels)
20439 * @cfg {Number} width (in pixels)
20444 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20447 stylesheets: false,
20452 // private properties
20453 validationEvent : false,
20455 initialized : false,
20457 sourceEditMode : false,
20458 onFocus : Roo.emptyFn,
20460 hideMode:'offsets',
20464 // blacklist + whitelisted elements..
20471 * Protected method that will not generally be called directly. It
20472 * is called when the editor initializes the iframe with HTML contents. Override this method if you
20473 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20475 getDocMarkup : function(){
20479 // inherit styels from page...??
20480 if (this.stylesheets === false) {
20482 Roo.get(document.head).select('style').each(function(node) {
20483 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20486 Roo.get(document.head).select('link').each(function(node) {
20487 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20490 } else if (!this.stylesheets.length) {
20492 st = '<style type="text/css">' +
20493 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20499 st += '<style type="text/css">' +
20500 'IMG { cursor: pointer } ' +
20504 return '<html><head>' + st +
20505 //<style type="text/css">' +
20506 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20508 ' </head><body class="roo-htmleditor-body"></body></html>';
20512 onRender : function(ct, position)
20515 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20516 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20519 this.el.dom.style.border = '0 none';
20520 this.el.dom.setAttribute('tabIndex', -1);
20521 this.el.addClass('x-hidden hide');
20525 if(Roo.isIE){ // fix IE 1px bogus margin
20526 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20530 this.frameId = Roo.id();
20534 var iframe = this.owner.wrap.createChild({
20536 cls: 'form-control', // bootstrap..
20538 name: this.frameId,
20539 frameBorder : 'no',
20540 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
20545 this.iframe = iframe.dom;
20547 this.assignDocWin();
20549 this.doc.designMode = 'on';
20552 this.doc.write(this.getDocMarkup());
20556 var task = { // must defer to wait for browser to be ready
20558 //console.log("run task?" + this.doc.readyState);
20559 this.assignDocWin();
20560 if(this.doc.body || this.doc.readyState == 'complete'){
20562 this.doc.designMode="on";
20566 Roo.TaskMgr.stop(task);
20567 this.initEditor.defer(10, this);
20574 Roo.TaskMgr.start(task);
20579 onResize : function(w, h)
20581 Roo.log('resize: ' +w + ',' + h );
20582 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20586 if(typeof w == 'number'){
20588 this.iframe.style.width = w + 'px';
20590 if(typeof h == 'number'){
20592 this.iframe.style.height = h + 'px';
20594 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20601 * Toggles the editor between standard and source edit mode.
20602 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20604 toggleSourceEdit : function(sourceEditMode){
20606 this.sourceEditMode = sourceEditMode === true;
20608 if(this.sourceEditMode){
20610 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
20613 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20614 //this.iframe.className = '';
20617 //this.setSize(this.owner.wrap.getSize());
20618 //this.fireEvent('editmodechange', this, this.sourceEditMode);
20625 * Protected method that will not generally be called directly. If you need/want
20626 * custom HTML cleanup, this is the method you should override.
20627 * @param {String} html The HTML to be cleaned
20628 * return {String} The cleaned HTML
20630 cleanHtml : function(html){
20631 html = String(html);
20632 if(html.length > 5){
20633 if(Roo.isSafari){ // strip safari nonsense
20634 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20637 if(html == ' '){
20644 * HTML Editor -> Textarea
20645 * Protected method that will not generally be called directly. Syncs the contents
20646 * of the editor iframe with the textarea.
20648 syncValue : function(){
20649 if(this.initialized){
20650 var bd = (this.doc.body || this.doc.documentElement);
20651 //this.cleanUpPaste(); -- this is done else where and causes havoc..
20652 var html = bd.innerHTML;
20654 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20655 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20657 html = '<div style="'+m[0]+'">' + html + '</div>';
20660 html = this.cleanHtml(html);
20661 // fix up the special chars.. normaly like back quotes in word...
20662 // however we do not want to do this with chinese..
20663 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20664 var cc = b.charCodeAt();
20666 (cc >= 0x4E00 && cc < 0xA000 ) ||
20667 (cc >= 0x3400 && cc < 0x4E00 ) ||
20668 (cc >= 0xf900 && cc < 0xfb00 )
20674 if(this.owner.fireEvent('beforesync', this, html) !== false){
20675 this.el.dom.value = html;
20676 this.owner.fireEvent('sync', this, html);
20682 * Protected method that will not generally be called directly. Pushes the value of the textarea
20683 * into the iframe editor.
20685 pushValue : function(){
20686 if(this.initialized){
20687 var v = this.el.dom.value.trim();
20689 // if(v.length < 1){
20693 if(this.owner.fireEvent('beforepush', this, v) !== false){
20694 var d = (this.doc.body || this.doc.documentElement);
20696 this.cleanUpPaste();
20697 this.el.dom.value = d.innerHTML;
20698 this.owner.fireEvent('push', this, v);
20704 deferFocus : function(){
20705 this.focus.defer(10, this);
20709 focus : function(){
20710 if(this.win && !this.sourceEditMode){
20717 assignDocWin: function()
20719 var iframe = this.iframe;
20722 this.doc = iframe.contentWindow.document;
20723 this.win = iframe.contentWindow;
20725 // if (!Roo.get(this.frameId)) {
20728 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20729 // this.win = Roo.get(this.frameId).dom.contentWindow;
20731 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20735 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20736 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20741 initEditor : function(){
20742 //console.log("INIT EDITOR");
20743 this.assignDocWin();
20747 this.doc.designMode="on";
20749 this.doc.write(this.getDocMarkup());
20752 var dbody = (this.doc.body || this.doc.documentElement);
20753 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20754 // this copies styles from the containing element into thsi one..
20755 // not sure why we need all of this..
20756 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20758 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20759 //ss['background-attachment'] = 'fixed'; // w3c
20760 dbody.bgProperties = 'fixed'; // ie
20761 //Roo.DomHelper.applyStyles(dbody, ss);
20762 Roo.EventManager.on(this.doc, {
20763 //'mousedown': this.onEditorEvent,
20764 'mouseup': this.onEditorEvent,
20765 'dblclick': this.onEditorEvent,
20766 'click': this.onEditorEvent,
20767 'keyup': this.onEditorEvent,
20772 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20774 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20775 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20777 this.initialized = true;
20779 this.owner.fireEvent('initialize', this);
20784 onDestroy : function(){
20790 //for (var i =0; i < this.toolbars.length;i++) {
20791 // // fixme - ask toolbars for heights?
20792 // this.toolbars[i].onDestroy();
20795 //this.wrap.dom.innerHTML = '';
20796 //this.wrap.remove();
20801 onFirstFocus : function(){
20803 this.assignDocWin();
20806 this.activated = true;
20809 if(Roo.isGecko){ // prevent silly gecko errors
20811 var s = this.win.getSelection();
20812 if(!s.focusNode || s.focusNode.nodeType != 3){
20813 var r = s.getRangeAt(0);
20814 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20819 this.execCmd('useCSS', true);
20820 this.execCmd('styleWithCSS', false);
20823 this.owner.fireEvent('activate', this);
20827 adjustFont: function(btn){
20828 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20829 //if(Roo.isSafari){ // safari
20832 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20833 if(Roo.isSafari){ // safari
20834 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20835 v = (v < 10) ? 10 : v;
20836 v = (v > 48) ? 48 : v;
20837 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20842 v = Math.max(1, v+adjust);
20844 this.execCmd('FontSize', v );
20847 onEditorEvent : function(e)
20849 this.owner.fireEvent('editorevent', this, e);
20850 // this.updateToolbar();
20851 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20854 insertTag : function(tg)
20856 // could be a bit smarter... -> wrap the current selected tRoo..
20857 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20859 range = this.createRange(this.getSelection());
20860 var wrappingNode = this.doc.createElement(tg.toLowerCase());
20861 wrappingNode.appendChild(range.extractContents());
20862 range.insertNode(wrappingNode);
20869 this.execCmd("formatblock", tg);
20873 insertText : function(txt)
20877 var range = this.createRange();
20878 range.deleteContents();
20879 //alert(Sender.getAttribute('label'));
20881 range.insertNode(this.doc.createTextNode(txt));
20887 * Executes a Midas editor command on the editor document and performs necessary focus and
20888 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20889 * @param {String} cmd The Midas command
20890 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20892 relayCmd : function(cmd, value){
20894 this.execCmd(cmd, value);
20895 this.owner.fireEvent('editorevent', this);
20896 //this.updateToolbar();
20897 this.owner.deferFocus();
20901 * Executes a Midas editor command directly on the editor document.
20902 * For visual commands, you should use {@link #relayCmd} instead.
20903 * <b>This should only be called after the editor is initialized.</b>
20904 * @param {String} cmd The Midas command
20905 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20907 execCmd : function(cmd, value){
20908 this.doc.execCommand(cmd, false, value === undefined ? null : value);
20915 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20917 * @param {String} text | dom node..
20919 insertAtCursor : function(text)
20924 if(!this.activated){
20930 var r = this.doc.selection.createRange();
20941 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20945 // from jquery ui (MIT licenced)
20947 var win = this.win;
20949 if (win.getSelection && win.getSelection().getRangeAt) {
20950 range = win.getSelection().getRangeAt(0);
20951 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20952 range.insertNode(node);
20953 } else if (win.document.selection && win.document.selection.createRange) {
20954 // no firefox support
20955 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20956 win.document.selection.createRange().pasteHTML(txt);
20958 // no firefox support
20959 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20960 this.execCmd('InsertHTML', txt);
20969 mozKeyPress : function(e){
20971 var c = e.getCharCode(), cmd;
20974 c = String.fromCharCode(c).toLowerCase();
20988 this.cleanUpPaste.defer(100, this);
20996 e.preventDefault();
21004 fixKeys : function(){ // load time branching for fastest keydown performance
21006 return function(e){
21007 var k = e.getKey(), r;
21010 r = this.doc.selection.createRange();
21013 r.pasteHTML('    ');
21020 r = this.doc.selection.createRange();
21022 var target = r.parentElement();
21023 if(!target || target.tagName.toLowerCase() != 'li'){
21025 r.pasteHTML('<br />');
21031 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21032 this.cleanUpPaste.defer(100, this);
21038 }else if(Roo.isOpera){
21039 return function(e){
21040 var k = e.getKey();
21044 this.execCmd('InsertHTML','    ');
21047 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21048 this.cleanUpPaste.defer(100, this);
21053 }else if(Roo.isSafari){
21054 return function(e){
21055 var k = e.getKey();
21059 this.execCmd('InsertText','\t');
21063 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21064 this.cleanUpPaste.defer(100, this);
21072 getAllAncestors: function()
21074 var p = this.getSelectedNode();
21077 a.push(p); // push blank onto stack..
21078 p = this.getParentElement();
21082 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21086 a.push(this.doc.body);
21090 lastSelNode : false,
21093 getSelection : function()
21095 this.assignDocWin();
21096 return Roo.isIE ? this.doc.selection : this.win.getSelection();
21099 getSelectedNode: function()
21101 // this may only work on Gecko!!!
21103 // should we cache this!!!!
21108 var range = this.createRange(this.getSelection()).cloneRange();
21111 var parent = range.parentElement();
21113 var testRange = range.duplicate();
21114 testRange.moveToElementText(parent);
21115 if (testRange.inRange(range)) {
21118 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21121 parent = parent.parentElement;
21126 // is ancestor a text element.
21127 var ac = range.commonAncestorContainer;
21128 if (ac.nodeType == 3) {
21129 ac = ac.parentNode;
21132 var ar = ac.childNodes;
21135 var other_nodes = [];
21136 var has_other_nodes = false;
21137 for (var i=0;i<ar.length;i++) {
21138 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
21141 // fullly contained node.
21143 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21148 // probably selected..
21149 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21150 other_nodes.push(ar[i]);
21154 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
21159 has_other_nodes = true;
21161 if (!nodes.length && other_nodes.length) {
21162 nodes= other_nodes;
21164 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21170 createRange: function(sel)
21172 // this has strange effects when using with
21173 // top toolbar - not sure if it's a great idea.
21174 //this.editor.contentWindow.focus();
21175 if (typeof sel != "undefined") {
21177 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21179 return this.doc.createRange();
21182 return this.doc.createRange();
21185 getParentElement: function()
21188 this.assignDocWin();
21189 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21191 var range = this.createRange(sel);
21194 var p = range.commonAncestorContainer;
21195 while (p.nodeType == 3) { // text node
21206 * Range intersection.. the hard stuff...
21210 * [ -- selected range --- ]
21214 * if end is before start or hits it. fail.
21215 * if start is after end or hits it fail.
21217 * if either hits (but other is outside. - then it's not
21223 // @see http://www.thismuchiknow.co.uk/?p=64.
21224 rangeIntersectsNode : function(range, node)
21226 var nodeRange = node.ownerDocument.createRange();
21228 nodeRange.selectNode(node);
21230 nodeRange.selectNodeContents(node);
21233 var rangeStartRange = range.cloneRange();
21234 rangeStartRange.collapse(true);
21236 var rangeEndRange = range.cloneRange();
21237 rangeEndRange.collapse(false);
21239 var nodeStartRange = nodeRange.cloneRange();
21240 nodeStartRange.collapse(true);
21242 var nodeEndRange = nodeRange.cloneRange();
21243 nodeEndRange.collapse(false);
21245 return rangeStartRange.compareBoundaryPoints(
21246 Range.START_TO_START, nodeEndRange) == -1 &&
21247 rangeEndRange.compareBoundaryPoints(
21248 Range.START_TO_START, nodeStartRange) == 1;
21252 rangeCompareNode : function(range, node)
21254 var nodeRange = node.ownerDocument.createRange();
21256 nodeRange.selectNode(node);
21258 nodeRange.selectNodeContents(node);
21262 range.collapse(true);
21264 nodeRange.collapse(true);
21266 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21267 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
21269 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21271 var nodeIsBefore = ss == 1;
21272 var nodeIsAfter = ee == -1;
21274 if (nodeIsBefore && nodeIsAfter) {
21277 if (!nodeIsBefore && nodeIsAfter) {
21278 return 1; //right trailed.
21281 if (nodeIsBefore && !nodeIsAfter) {
21282 return 2; // left trailed.
21288 // private? - in a new class?
21289 cleanUpPaste : function()
21291 // cleans up the whole document..
21292 Roo.log('cleanuppaste');
21294 this.cleanUpChildren(this.doc.body);
21295 var clean = this.cleanWordChars(this.doc.body.innerHTML);
21296 if (clean != this.doc.body.innerHTML) {
21297 this.doc.body.innerHTML = clean;
21302 cleanWordChars : function(input) {// change the chars to hex code
21303 var he = Roo.HtmlEditorCore;
21305 var output = input;
21306 Roo.each(he.swapCodes, function(sw) {
21307 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21309 output = output.replace(swapper, sw[1]);
21316 cleanUpChildren : function (n)
21318 if (!n.childNodes.length) {
21321 for (var i = n.childNodes.length-1; i > -1 ; i--) {
21322 this.cleanUpChild(n.childNodes[i]);
21329 cleanUpChild : function (node)
21332 //console.log(node);
21333 if (node.nodeName == "#text") {
21334 // clean up silly Windows -- stuff?
21337 if (node.nodeName == "#comment") {
21338 node.parentNode.removeChild(node);
21339 // clean up silly Windows -- stuff?
21342 var lcname = node.tagName.toLowerCase();
21343 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21344 // whitelist of tags..
21346 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21348 node.parentNode.removeChild(node);
21353 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21355 // remove <a name=....> as rendering on yahoo mailer is borked with this.
21356 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21358 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21359 // remove_keep_children = true;
21362 if (remove_keep_children) {
21363 this.cleanUpChildren(node);
21364 // inserts everything just before this node...
21365 while (node.childNodes.length) {
21366 var cn = node.childNodes[0];
21367 node.removeChild(cn);
21368 node.parentNode.insertBefore(cn, node);
21370 node.parentNode.removeChild(node);
21374 if (!node.attributes || !node.attributes.length) {
21375 this.cleanUpChildren(node);
21379 function cleanAttr(n,v)
21382 if (v.match(/^\./) || v.match(/^\//)) {
21385 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21388 if (v.match(/^#/)) {
21391 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21392 node.removeAttribute(n);
21396 var cwhite = this.cwhite;
21397 var cblack = this.cblack;
21399 function cleanStyle(n,v)
21401 if (v.match(/expression/)) { //XSS?? should we even bother..
21402 node.removeAttribute(n);
21406 var parts = v.split(/;/);
21409 Roo.each(parts, function(p) {
21410 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21414 var l = p.split(':').shift().replace(/\s+/g,'');
21415 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21417 if ( cwhite.length && cblack.indexOf(l) > -1) {
21418 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21419 //node.removeAttribute(n);
21423 // only allow 'c whitelisted system attributes'
21424 if ( cwhite.length && cwhite.indexOf(l) < 0) {
21425 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21426 //node.removeAttribute(n);
21436 if (clean.length) {
21437 node.setAttribute(n, clean.join(';'));
21439 node.removeAttribute(n);
21445 for (var i = node.attributes.length-1; i > -1 ; i--) {
21446 var a = node.attributes[i];
21449 if (a.name.toLowerCase().substr(0,2)=='on') {
21450 node.removeAttribute(a.name);
21453 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21454 node.removeAttribute(a.name);
21457 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21458 cleanAttr(a.name,a.value); // fixme..
21461 if (a.name == 'style') {
21462 cleanStyle(a.name,a.value);
21465 /// clean up MS crap..
21466 // tecnically this should be a list of valid class'es..
21469 if (a.name == 'class') {
21470 if (a.value.match(/^Mso/)) {
21471 node.className = '';
21474 if (a.value.match(/body/)) {
21475 node.className = '';
21486 this.cleanUpChildren(node);
21492 * Clean up MS wordisms...
21494 cleanWord : function(node)
21499 this.cleanWord(this.doc.body);
21502 if (node.nodeName == "#text") {
21503 // clean up silly Windows -- stuff?
21506 if (node.nodeName == "#comment") {
21507 node.parentNode.removeChild(node);
21508 // clean up silly Windows -- stuff?
21512 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21513 node.parentNode.removeChild(node);
21517 // remove - but keep children..
21518 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21519 while (node.childNodes.length) {
21520 var cn = node.childNodes[0];
21521 node.removeChild(cn);
21522 node.parentNode.insertBefore(cn, node);
21524 node.parentNode.removeChild(node);
21525 this.iterateChildren(node, this.cleanWord);
21529 if (node.className.length) {
21531 var cn = node.className.split(/\W+/);
21533 Roo.each(cn, function(cls) {
21534 if (cls.match(/Mso[a-zA-Z]+/)) {
21539 node.className = cna.length ? cna.join(' ') : '';
21541 node.removeAttribute("class");
21545 if (node.hasAttribute("lang")) {
21546 node.removeAttribute("lang");
21549 if (node.hasAttribute("style")) {
21551 var styles = node.getAttribute("style").split(";");
21553 Roo.each(styles, function(s) {
21554 if (!s.match(/:/)) {
21557 var kv = s.split(":");
21558 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21561 // what ever is left... we allow.
21564 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21565 if (!nstyle.length) {
21566 node.removeAttribute('style');
21569 this.iterateChildren(node, this.cleanWord);
21575 * iterateChildren of a Node, calling fn each time, using this as the scole..
21576 * @param {DomNode} node node to iterate children of.
21577 * @param {Function} fn method of this class to call on each item.
21579 iterateChildren : function(node, fn)
21581 if (!node.childNodes.length) {
21584 for (var i = node.childNodes.length-1; i > -1 ; i--) {
21585 fn.call(this, node.childNodes[i])
21591 * cleanTableWidths.
21593 * Quite often pasting from word etc.. results in tables with column and widths.
21594 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21597 cleanTableWidths : function(node)
21602 this.cleanTableWidths(this.doc.body);
21607 if (node.nodeName == "#text" || node.nodeName == "#comment") {
21610 Roo.log(node.tagName);
21611 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21612 this.iterateChildren(node, this.cleanTableWidths);
21615 if (node.hasAttribute('width')) {
21616 node.removeAttribute('width');
21620 if (node.hasAttribute("style")) {
21623 var styles = node.getAttribute("style").split(";");
21625 Roo.each(styles, function(s) {
21626 if (!s.match(/:/)) {
21629 var kv = s.split(":");
21630 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21633 // what ever is left... we allow.
21636 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21637 if (!nstyle.length) {
21638 node.removeAttribute('style');
21642 this.iterateChildren(node, this.cleanTableWidths);
21650 domToHTML : function(currentElement, depth, nopadtext) {
21652 depth = depth || 0;
21653 nopadtext = nopadtext || false;
21655 if (!currentElement) {
21656 return this.domToHTML(this.doc.body);
21659 //Roo.log(currentElement);
21661 var allText = false;
21662 var nodeName = currentElement.nodeName;
21663 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21665 if (nodeName == '#text') {
21667 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21672 if (nodeName != 'BODY') {
21675 // Prints the node tagName, such as <A>, <IMG>, etc
21678 for(i = 0; i < currentElement.attributes.length;i++) {
21680 var aname = currentElement.attributes.item(i).name;
21681 if (!currentElement.attributes.item(i).value.length) {
21684 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21687 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21696 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21699 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21704 // Traverse the tree
21706 var currentElementChild = currentElement.childNodes.item(i);
21707 var allText = true;
21708 var innerHTML = '';
21710 while (currentElementChild) {
21711 // Formatting code (indent the tree so it looks nice on the screen)
21712 var nopad = nopadtext;
21713 if (lastnode == 'SPAN') {
21717 if (currentElementChild.nodeName == '#text') {
21718 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21719 toadd = nopadtext ? toadd : toadd.trim();
21720 if (!nopad && toadd.length > 80) {
21721 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
21723 innerHTML += toadd;
21726 currentElementChild = currentElement.childNodes.item(i);
21732 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
21734 // Recursively traverse the tree structure of the child node
21735 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
21736 lastnode = currentElementChild.nodeName;
21738 currentElementChild=currentElement.childNodes.item(i);
21744 // The remaining code is mostly for formatting the tree
21745 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
21750 ret+= "</"+tagName+">";
21756 applyBlacklists : function()
21758 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
21759 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
21763 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21764 if (b.indexOf(tag) > -1) {
21767 this.white.push(tag);
21771 Roo.each(w, function(tag) {
21772 if (b.indexOf(tag) > -1) {
21775 if (this.white.indexOf(tag) > -1) {
21778 this.white.push(tag);
21783 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21784 if (w.indexOf(tag) > -1) {
21787 this.black.push(tag);
21791 Roo.each(b, function(tag) {
21792 if (w.indexOf(tag) > -1) {
21795 if (this.black.indexOf(tag) > -1) {
21798 this.black.push(tag);
21803 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
21804 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
21808 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21809 if (b.indexOf(tag) > -1) {
21812 this.cwhite.push(tag);
21816 Roo.each(w, function(tag) {
21817 if (b.indexOf(tag) > -1) {
21820 if (this.cwhite.indexOf(tag) > -1) {
21823 this.cwhite.push(tag);
21828 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21829 if (w.indexOf(tag) > -1) {
21832 this.cblack.push(tag);
21836 Roo.each(b, function(tag) {
21837 if (w.indexOf(tag) > -1) {
21840 if (this.cblack.indexOf(tag) > -1) {
21843 this.cblack.push(tag);
21848 setStylesheets : function(stylesheets)
21850 if(typeof(stylesheets) == 'string'){
21851 Roo.get(this.iframe.contentDocument.head).createChild({
21853 rel : 'stylesheet',
21862 Roo.each(stylesheets, function(s) {
21867 Roo.get(_this.iframe.contentDocument.head).createChild({
21869 rel : 'stylesheet',
21878 removeStylesheets : function()
21882 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21887 // hide stuff that is not compatible
21901 * @event specialkey
21905 * @cfg {String} fieldClass @hide
21908 * @cfg {String} focusClass @hide
21911 * @cfg {String} autoCreate @hide
21914 * @cfg {String} inputType @hide
21917 * @cfg {String} invalidClass @hide
21920 * @cfg {String} invalidText @hide
21923 * @cfg {String} msgFx @hide
21926 * @cfg {String} validateOnBlur @hide
21930 Roo.HtmlEditorCore.white = [
21931 'area', 'br', 'img', 'input', 'hr', 'wbr',
21933 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
21934 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
21935 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
21936 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
21937 'table', 'ul', 'xmp',
21939 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
21942 'dir', 'menu', 'ol', 'ul', 'dl',
21948 Roo.HtmlEditorCore.black = [
21949 // 'embed', 'object', // enable - backend responsiblity to clean thiese
21951 'base', 'basefont', 'bgsound', 'blink', 'body',
21952 'frame', 'frameset', 'head', 'html', 'ilayer',
21953 'iframe', 'layer', 'link', 'meta', 'object',
21954 'script', 'style' ,'title', 'xml' // clean later..
21956 Roo.HtmlEditorCore.clean = [
21957 'script', 'style', 'title', 'xml'
21959 Roo.HtmlEditorCore.remove = [
21964 Roo.HtmlEditorCore.ablack = [
21968 Roo.HtmlEditorCore.aclean = [
21969 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
21973 Roo.HtmlEditorCore.pwhite= [
21974 'http', 'https', 'mailto'
21977 // white listed style attributes.
21978 Roo.HtmlEditorCore.cwhite= [
21979 // 'text-align', /// default is to allow most things..
21985 // black listed style attributes.
21986 Roo.HtmlEditorCore.cblack= [
21987 // 'font-size' -- this can be set by the project
21991 Roo.HtmlEditorCore.swapCodes =[
22010 * @class Roo.bootstrap.HtmlEditor
22011 * @extends Roo.bootstrap.TextArea
22012 * Bootstrap HtmlEditor class
22015 * Create a new HtmlEditor
22016 * @param {Object} config The config object
22019 Roo.bootstrap.HtmlEditor = function(config){
22020 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22021 if (!this.toolbars) {
22022 this.toolbars = [];
22024 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22027 * @event initialize
22028 * Fires when the editor is fully initialized (including the iframe)
22029 * @param {HtmlEditor} this
22034 * Fires when the editor is first receives the focus. Any insertion must wait
22035 * until after this event.
22036 * @param {HtmlEditor} this
22040 * @event beforesync
22041 * Fires before the textarea is updated with content from the editor iframe. Return false
22042 * to cancel the sync.
22043 * @param {HtmlEditor} this
22044 * @param {String} html
22048 * @event beforepush
22049 * Fires before the iframe editor is updated with content from the textarea. Return false
22050 * to cancel the push.
22051 * @param {HtmlEditor} this
22052 * @param {String} html
22057 * Fires when the textarea is updated with content from the editor iframe.
22058 * @param {HtmlEditor} this
22059 * @param {String} html
22064 * Fires when the iframe editor is updated with content from the textarea.
22065 * @param {HtmlEditor} this
22066 * @param {String} html
22070 * @event editmodechange
22071 * Fires when the editor switches edit modes
22072 * @param {HtmlEditor} this
22073 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22075 editmodechange: true,
22077 * @event editorevent
22078 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22079 * @param {HtmlEditor} this
22083 * @event firstfocus
22084 * Fires when on first focus - needed by toolbars..
22085 * @param {HtmlEditor} this
22090 * Auto save the htmlEditor value as a file into Events
22091 * @param {HtmlEditor} this
22095 * @event savedpreview
22096 * preview the saved version of htmlEditor
22097 * @param {HtmlEditor} this
22104 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
22108 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22113 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22118 * @cfg {Number} height (in pixels)
22122 * @cfg {Number} width (in pixels)
22127 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22130 stylesheets: false,
22135 // private properties
22136 validationEvent : false,
22138 initialized : false,
22141 onFocus : Roo.emptyFn,
22143 hideMode:'offsets',
22146 tbContainer : false,
22148 toolbarContainer :function() {
22149 return this.wrap.select('.x-html-editor-tb',true).first();
22153 * Protected method that will not generally be called directly. It
22154 * is called when the editor creates its toolbar. Override this method if you need to
22155 * add custom toolbar buttons.
22156 * @param {HtmlEditor} editor
22158 createToolbar : function(){
22160 Roo.log("create toolbars");
22162 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22163 this.toolbars[0].render(this.toolbarContainer());
22167 // if (!editor.toolbars || !editor.toolbars.length) {
22168 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22171 // for (var i =0 ; i < editor.toolbars.length;i++) {
22172 // editor.toolbars[i] = Roo.factory(
22173 // typeof(editor.toolbars[i]) == 'string' ?
22174 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
22175 // Roo.bootstrap.HtmlEditor);
22176 // editor.toolbars[i].init(editor);
22182 onRender : function(ct, position)
22184 // Roo.log("Call onRender: " + this.xtype);
22186 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22188 this.wrap = this.inputEl().wrap({
22189 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22192 this.editorcore.onRender(ct, position);
22194 if (this.resizable) {
22195 this.resizeEl = new Roo.Resizable(this.wrap, {
22199 minHeight : this.height,
22200 height: this.height,
22201 handles : this.resizable,
22204 resize : function(r, w, h) {
22205 _t.onResize(w,h); // -something
22211 this.createToolbar(this);
22214 if(!this.width && this.resizable){
22215 this.setSize(this.wrap.getSize());
22217 if (this.resizeEl) {
22218 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22219 // should trigger onReize..
22225 onResize : function(w, h)
22227 Roo.log('resize: ' +w + ',' + h );
22228 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22232 if(this.inputEl() ){
22233 if(typeof w == 'number'){
22234 var aw = w - this.wrap.getFrameWidth('lr');
22235 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22238 if(typeof h == 'number'){
22239 var tbh = -11; // fixme it needs to tool bar size!
22240 for (var i =0; i < this.toolbars.length;i++) {
22241 // fixme - ask toolbars for heights?
22242 tbh += this.toolbars[i].el.getHeight();
22243 //if (this.toolbars[i].footer) {
22244 // tbh += this.toolbars[i].footer.el.getHeight();
22252 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22253 ah -= 5; // knock a few pixes off for look..
22254 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
22258 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22259 this.editorcore.onResize(ew,eh);
22264 * Toggles the editor between standard and source edit mode.
22265 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22267 toggleSourceEdit : function(sourceEditMode)
22269 this.editorcore.toggleSourceEdit(sourceEditMode);
22271 if(this.editorcore.sourceEditMode){
22272 Roo.log('editor - showing textarea');
22275 // Roo.log(this.syncValue());
22277 this.inputEl().removeClass(['hide', 'x-hidden']);
22278 this.inputEl().dom.removeAttribute('tabIndex');
22279 this.inputEl().focus();
22281 Roo.log('editor - hiding textarea');
22283 // Roo.log(this.pushValue());
22286 this.inputEl().addClass(['hide', 'x-hidden']);
22287 this.inputEl().dom.setAttribute('tabIndex', -1);
22288 //this.deferFocus();
22291 if(this.resizable){
22292 this.setSize(this.wrap.getSize());
22295 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22298 // private (for BoxComponent)
22299 adjustSize : Roo.BoxComponent.prototype.adjustSize,
22301 // private (for BoxComponent)
22302 getResizeEl : function(){
22306 // private (for BoxComponent)
22307 getPositionEl : function(){
22312 initEvents : function(){
22313 this.originalValue = this.getValue();
22317 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22320 // markInvalid : Roo.emptyFn,
22322 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22325 // clearInvalid : Roo.emptyFn,
22327 setValue : function(v){
22328 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
22329 this.editorcore.pushValue();
22334 deferFocus : function(){
22335 this.focus.defer(10, this);
22339 focus : function(){
22340 this.editorcore.focus();
22346 onDestroy : function(){
22352 for (var i =0; i < this.toolbars.length;i++) {
22353 // fixme - ask toolbars for heights?
22354 this.toolbars[i].onDestroy();
22357 this.wrap.dom.innerHTML = '';
22358 this.wrap.remove();
22363 onFirstFocus : function(){
22364 //Roo.log("onFirstFocus");
22365 this.editorcore.onFirstFocus();
22366 for (var i =0; i < this.toolbars.length;i++) {
22367 this.toolbars[i].onFirstFocus();
22373 syncValue : function()
22375 this.editorcore.syncValue();
22378 pushValue : function()
22380 this.editorcore.pushValue();
22384 // hide stuff that is not compatible
22398 * @event specialkey
22402 * @cfg {String} fieldClass @hide
22405 * @cfg {String} focusClass @hide
22408 * @cfg {String} autoCreate @hide
22411 * @cfg {String} inputType @hide
22414 * @cfg {String} invalidClass @hide
22417 * @cfg {String} invalidText @hide
22420 * @cfg {String} msgFx @hide
22423 * @cfg {String} validateOnBlur @hide
22432 Roo.namespace('Roo.bootstrap.htmleditor');
22434 * @class Roo.bootstrap.HtmlEditorToolbar1
22439 new Roo.bootstrap.HtmlEditor({
22442 new Roo.bootstrap.HtmlEditorToolbar1({
22443 disable : { fonts: 1 , format: 1, ..., ... , ...],
22449 * @cfg {Object} disable List of elements to disable..
22450 * @cfg {Array} btns List of additional buttons.
22454 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22457 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22460 Roo.apply(this, config);
22462 // default disabled, based on 'good practice'..
22463 this.disable = this.disable || {};
22464 Roo.applyIf(this.disable, {
22467 specialElements : true
22469 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22471 this.editor = config.editor;
22472 this.editorcore = config.editor.editorcore;
22474 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22476 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22477 // dont call parent... till later.
22479 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
22484 editorcore : false,
22489 "h1","h2","h3","h4","h5","h6",
22491 "abbr", "acronym", "address", "cite", "samp", "var",
22495 onRender : function(ct, position)
22497 // Roo.log("Call onRender: " + this.xtype);
22499 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22501 this.el.dom.style.marginBottom = '0';
22503 var editorcore = this.editorcore;
22504 var editor= this.editor;
22507 var btn = function(id,cmd , toggle, handler){
22509 var event = toggle ? 'toggle' : 'click';
22514 xns: Roo.bootstrap,
22517 enableToggle:toggle !== false,
22519 pressed : toggle ? false : null,
22522 a.listeners[toggle ? 'toggle' : 'click'] = function() {
22523 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
22532 xns: Roo.bootstrap,
22533 glyphicon : 'font',
22537 xns: Roo.bootstrap,
22541 Roo.each(this.formats, function(f) {
22542 style.menu.items.push({
22544 xns: Roo.bootstrap,
22545 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22550 editorcore.insertTag(this.tagname);
22557 children.push(style);
22560 btn('bold',false,true);
22561 btn('italic',false,true);
22562 btn('align-left', 'justifyleft',true);
22563 btn('align-center', 'justifycenter',true);
22564 btn('align-right' , 'justifyright',true);
22565 btn('link', false, false, function(btn) {
22566 //Roo.log("create link?");
22567 var url = prompt(this.createLinkText, this.defaultLinkValue);
22568 if(url && url != 'http:/'+'/'){
22569 this.editorcore.relayCmd('createlink', url);
22572 btn('list','insertunorderedlist',true);
22573 btn('pencil', false,true, function(btn){
22576 this.toggleSourceEdit(btn.pressed);
22582 xns: Roo.bootstrap,
22587 xns: Roo.bootstrap,
22592 cog.menu.items.push({
22594 xns: Roo.bootstrap,
22595 html : Clean styles,
22600 editorcore.insertTag(this.tagname);
22609 this.xtype = 'NavSimplebar';
22611 for(var i=0;i< children.length;i++) {
22613 this.buttons.add(this.addxtypeChild(children[i]));
22617 editor.on('editorevent', this.updateToolbar, this);
22619 onBtnClick : function(id)
22621 this.editorcore.relayCmd(id);
22622 this.editorcore.focus();
22626 * Protected method that will not generally be called directly. It triggers
22627 * a toolbar update by reading the markup state of the current selection in the editor.
22629 updateToolbar: function(){
22631 if(!this.editorcore.activated){
22632 this.editor.onFirstFocus(); // is this neeed?
22636 var btns = this.buttons;
22637 var doc = this.editorcore.doc;
22638 btns.get('bold').setActive(doc.queryCommandState('bold'));
22639 btns.get('italic').setActive(doc.queryCommandState('italic'));
22640 //btns.get('underline').setActive(doc.queryCommandState('underline'));
22642 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22643 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22644 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22646 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22647 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22650 var ans = this.editorcore.getAllAncestors();
22651 if (this.formatCombo) {
22654 var store = this.formatCombo.store;
22655 this.formatCombo.setValue("");
22656 for (var i =0; i < ans.length;i++) {
22657 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22659 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22667 // hides menus... - so this cant be on a menu...
22668 Roo.bootstrap.MenuMgr.hideAll();
22670 Roo.bootstrap.MenuMgr.hideAll();
22671 //this.editorsyncValue();
22673 onFirstFocus: function() {
22674 this.buttons.each(function(item){
22678 toggleSourceEdit : function(sourceEditMode){
22681 if(sourceEditMode){
22682 Roo.log("disabling buttons");
22683 this.buttons.each( function(item){
22684 if(item.cmd != 'pencil'){
22690 Roo.log("enabling buttons");
22691 if(this.editorcore.initialized){
22692 this.buttons.each( function(item){
22698 Roo.log("calling toggole on editor");
22699 // tell the editor that it's been pressed..
22700 this.editor.toggleSourceEdit(sourceEditMode);
22710 * @class Roo.bootstrap.Table.AbstractSelectionModel
22711 * @extends Roo.util.Observable
22712 * Abstract base class for grid SelectionModels. It provides the interface that should be
22713 * implemented by descendant classes. This class should not be directly instantiated.
22716 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22717 this.locked = false;
22718 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22722 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
22723 /** @ignore Called by the grid automatically. Do not call directly. */
22724 init : function(grid){
22730 * Locks the selections.
22733 this.locked = true;
22737 * Unlocks the selections.
22739 unlock : function(){
22740 this.locked = false;
22744 * Returns true if the selections are locked.
22745 * @return {Boolean}
22747 isLocked : function(){
22748 return this.locked;
22752 * @extends Roo.bootstrap.Table.AbstractSelectionModel
22753 * @class Roo.bootstrap.Table.RowSelectionModel
22754 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22755 * It supports multiple selections and keyboard selection/navigation.
22757 * @param {Object} config
22760 Roo.bootstrap.Table.RowSelectionModel = function(config){
22761 Roo.apply(this, config);
22762 this.selections = new Roo.util.MixedCollection(false, function(o){
22767 this.lastActive = false;
22771 * @event selectionchange
22772 * Fires when the selection changes
22773 * @param {SelectionModel} this
22775 "selectionchange" : true,
22777 * @event afterselectionchange
22778 * Fires after the selection changes (eg. by key press or clicking)
22779 * @param {SelectionModel} this
22781 "afterselectionchange" : true,
22783 * @event beforerowselect
22784 * Fires when a row is selected being selected, return false to cancel.
22785 * @param {SelectionModel} this
22786 * @param {Number} rowIndex The selected index
22787 * @param {Boolean} keepExisting False if other selections will be cleared
22789 "beforerowselect" : true,
22792 * Fires when a row is selected.
22793 * @param {SelectionModel} this
22794 * @param {Number} rowIndex The selected index
22795 * @param {Roo.data.Record} r The record
22797 "rowselect" : true,
22799 * @event rowdeselect
22800 * Fires when a row is deselected.
22801 * @param {SelectionModel} this
22802 * @param {Number} rowIndex The selected index
22804 "rowdeselect" : true
22806 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22807 this.locked = false;
22810 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
22812 * @cfg {Boolean} singleSelect
22813 * True to allow selection of only one row at a time (defaults to false)
22815 singleSelect : false,
22818 initEvents : function()
22821 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22822 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
22823 //}else{ // allow click to work like normal
22824 // this.grid.on("rowclick", this.handleDragableRowClick, this);
22826 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
22827 this.grid.on("rowclick", this.handleMouseDown, this);
22829 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22830 "up" : function(e){
22832 this.selectPrevious(e.shiftKey);
22833 }else if(this.last !== false && this.lastActive !== false){
22834 var last = this.last;
22835 this.selectRange(this.last, this.lastActive-1);
22836 this.grid.getView().focusRow(this.lastActive);
22837 if(last !== false){
22841 this.selectFirstRow();
22843 this.fireEvent("afterselectionchange", this);
22845 "down" : function(e){
22847 this.selectNext(e.shiftKey);
22848 }else if(this.last !== false && this.lastActive !== false){
22849 var last = this.last;
22850 this.selectRange(this.last, this.lastActive+1);
22851 this.grid.getView().focusRow(this.lastActive);
22852 if(last !== false){
22856 this.selectFirstRow();
22858 this.fireEvent("afterselectionchange", this);
22862 this.grid.store.on('load', function(){
22863 this.selections.clear();
22866 var view = this.grid.view;
22867 view.on("refresh", this.onRefresh, this);
22868 view.on("rowupdated", this.onRowUpdated, this);
22869 view.on("rowremoved", this.onRemove, this);
22874 onRefresh : function()
22876 var ds = this.grid.store, i, v = this.grid.view;
22877 var s = this.selections;
22878 s.each(function(r){
22879 if((i = ds.indexOfId(r.id)) != -1){
22888 onRemove : function(v, index, r){
22889 this.selections.remove(r);
22893 onRowUpdated : function(v, index, r){
22894 if(this.isSelected(r)){
22895 v.onRowSelect(index);
22901 * @param {Array} records The records to select
22902 * @param {Boolean} keepExisting (optional) True to keep existing selections
22904 selectRecords : function(records, keepExisting)
22907 this.clearSelections();
22909 var ds = this.grid.store;
22910 for(var i = 0, len = records.length; i < len; i++){
22911 this.selectRow(ds.indexOf(records[i]), true);
22916 * Gets the number of selected rows.
22919 getCount : function(){
22920 return this.selections.length;
22924 * Selects the first row in the grid.
22926 selectFirstRow : function(){
22931 * Select the last row.
22932 * @param {Boolean} keepExisting (optional) True to keep existing selections
22934 selectLastRow : function(keepExisting){
22935 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22936 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
22940 * Selects the row immediately following the last selected row.
22941 * @param {Boolean} keepExisting (optional) True to keep existing selections
22943 selectNext : function(keepExisting)
22945 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
22946 this.selectRow(this.last+1, keepExisting);
22947 this.grid.getView().focusRow(this.last);
22952 * Selects the row that precedes the last selected row.
22953 * @param {Boolean} keepExisting (optional) True to keep existing selections
22955 selectPrevious : function(keepExisting){
22957 this.selectRow(this.last-1, keepExisting);
22958 this.grid.getView().focusRow(this.last);
22963 * Returns the selected records
22964 * @return {Array} Array of selected records
22966 getSelections : function(){
22967 return [].concat(this.selections.items);
22971 * Returns the first selected record.
22974 getSelected : function(){
22975 return this.selections.itemAt(0);
22980 * Clears all selections.
22982 clearSelections : function(fast)
22988 var ds = this.grid.store;
22989 var s = this.selections;
22990 s.each(function(r){
22991 this.deselectRow(ds.indexOfId(r.id));
22995 this.selections.clear();
23002 * Selects all rows.
23004 selectAll : function(){
23008 this.selections.clear();
23009 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23010 this.selectRow(i, true);
23015 * Returns True if there is a selection.
23016 * @return {Boolean}
23018 hasSelection : function(){
23019 return this.selections.length > 0;
23023 * Returns True if the specified row is selected.
23024 * @param {Number/Record} record The record or index of the record to check
23025 * @return {Boolean}
23027 isSelected : function(index){
23028 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23029 return (r && this.selections.key(r.id) ? true : false);
23033 * Returns True if the specified record id is selected.
23034 * @param {String} id The id of record to check
23035 * @return {Boolean}
23037 isIdSelected : function(id){
23038 return (this.selections.key(id) ? true : false);
23043 handleMouseDBClick : function(e, t){
23047 handleMouseDown : function(e, t)
23049 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23050 if(this.isLocked() || rowIndex < 0 ){
23053 if(e.shiftKey && this.last !== false){
23054 var last = this.last;
23055 this.selectRange(last, rowIndex, e.ctrlKey);
23056 this.last = last; // reset the last
23060 var isSelected = this.isSelected(rowIndex);
23061 //Roo.log("select row:" + rowIndex);
23063 this.deselectRow(rowIndex);
23065 this.selectRow(rowIndex, true);
23069 if(e.button !== 0 && isSelected){
23070 alert('rowIndex 2: ' + rowIndex);
23071 view.focusRow(rowIndex);
23072 }else if(e.ctrlKey && isSelected){
23073 this.deselectRow(rowIndex);
23074 }else if(!isSelected){
23075 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23076 view.focusRow(rowIndex);
23080 this.fireEvent("afterselectionchange", this);
23083 handleDragableRowClick : function(grid, rowIndex, e)
23085 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23086 this.selectRow(rowIndex, false);
23087 grid.view.focusRow(rowIndex);
23088 this.fireEvent("afterselectionchange", this);
23093 * Selects multiple rows.
23094 * @param {Array} rows Array of the indexes of the row to select
23095 * @param {Boolean} keepExisting (optional) True to keep existing selections
23097 selectRows : function(rows, keepExisting){
23099 this.clearSelections();
23101 for(var i = 0, len = rows.length; i < len; i++){
23102 this.selectRow(rows[i], true);
23107 * Selects a range of rows. All rows in between startRow and endRow are also selected.
23108 * @param {Number} startRow The index of the first row in the range
23109 * @param {Number} endRow The index of the last row in the range
23110 * @param {Boolean} keepExisting (optional) True to retain existing selections
23112 selectRange : function(startRow, endRow, keepExisting){
23117 this.clearSelections();
23119 if(startRow <= endRow){
23120 for(var i = startRow; i <= endRow; i++){
23121 this.selectRow(i, true);
23124 for(var i = startRow; i >= endRow; i--){
23125 this.selectRow(i, true);
23131 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23132 * @param {Number} startRow The index of the first row in the range
23133 * @param {Number} endRow The index of the last row in the range
23135 deselectRange : function(startRow, endRow, preventViewNotify){
23139 for(var i = startRow; i <= endRow; i++){
23140 this.deselectRow(i, preventViewNotify);
23146 * @param {Number} row The index of the row to select
23147 * @param {Boolean} keepExisting (optional) True to keep existing selections
23149 selectRow : function(index, keepExisting, preventViewNotify)
23151 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23154 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23155 if(!keepExisting || this.singleSelect){
23156 this.clearSelections();
23159 var r = this.grid.store.getAt(index);
23160 //console.log('selectRow - record id :' + r.id);
23162 this.selections.add(r);
23163 this.last = this.lastActive = index;
23164 if(!preventViewNotify){
23165 var proxy = new Roo.Element(
23166 this.grid.getRowDom(index)
23168 proxy.addClass('bg-info info');
23170 this.fireEvent("rowselect", this, index, r);
23171 this.fireEvent("selectionchange", this);
23177 * @param {Number} row The index of the row to deselect
23179 deselectRow : function(index, preventViewNotify)
23184 if(this.last == index){
23187 if(this.lastActive == index){
23188 this.lastActive = false;
23191 var r = this.grid.store.getAt(index);
23196 this.selections.remove(r);
23197 //.console.log('deselectRow - record id :' + r.id);
23198 if(!preventViewNotify){
23200 var proxy = new Roo.Element(
23201 this.grid.getRowDom(index)
23203 proxy.removeClass('bg-info info');
23205 this.fireEvent("rowdeselect", this, index);
23206 this.fireEvent("selectionchange", this);
23210 restoreLast : function(){
23212 this.last = this._last;
23217 acceptsNav : function(row, col, cm){
23218 return !cm.isHidden(col) && cm.isCellEditable(col, row);
23222 onEditorKey : function(field, e){
23223 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23228 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23230 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23232 }else if(k == e.ENTER && !e.ctrlKey){
23236 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
23238 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
23240 }else if(k == e.ESC){
23244 g.startEditing(newCell[0], newCell[1]);
23250 * Ext JS Library 1.1.1
23251 * Copyright(c) 2006-2007, Ext JS, LLC.
23253 * Originally Released Under LGPL - original licence link has changed is not relivant.
23256 * <script type="text/javascript">
23260 * @class Roo.bootstrap.PagingToolbar
23261 * @extends Roo.bootstrap.NavSimplebar
23262 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
23264 * Create a new PagingToolbar
23265 * @param {Object} config The config object
23266 * @param {Roo.data.Store} store
23268 Roo.bootstrap.PagingToolbar = function(config)
23270 // old args format still supported... - xtype is prefered..
23271 // created from xtype...
23273 this.ds = config.dataSource;
23275 if (config.store && !this.ds) {
23276 this.store= Roo.factory(config.store, Roo.data);
23277 this.ds = this.store;
23278 this.ds.xmodule = this.xmodule || false;
23281 this.toolbarItems = [];
23282 if (config.items) {
23283 this.toolbarItems = config.items;
23286 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
23291 this.bind(this.ds);
23294 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
23298 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
23300 * @cfg {Roo.data.Store} dataSource
23301 * The underlying data store providing the paged data
23304 * @cfg {String/HTMLElement/Element} container
23305 * container The id or element that will contain the toolbar
23308 * @cfg {Boolean} displayInfo
23309 * True to display the displayMsg (defaults to false)
23312 * @cfg {Number} pageSize
23313 * The number of records to display per page (defaults to 20)
23317 * @cfg {String} displayMsg
23318 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
23320 displayMsg : 'Displaying {0} - {1} of {2}',
23322 * @cfg {String} emptyMsg
23323 * The message to display when no records are found (defaults to "No data to display")
23325 emptyMsg : 'No data to display',
23327 * Customizable piece of the default paging text (defaults to "Page")
23330 beforePageText : "Page",
23332 * Customizable piece of the default paging text (defaults to "of %0")
23335 afterPageText : "of {0}",
23337 * Customizable piece of the default paging text (defaults to "First Page")
23340 firstText : "First Page",
23342 * Customizable piece of the default paging text (defaults to "Previous Page")
23345 prevText : "Previous Page",
23347 * Customizable piece of the default paging text (defaults to "Next Page")
23350 nextText : "Next Page",
23352 * Customizable piece of the default paging text (defaults to "Last Page")
23355 lastText : "Last Page",
23357 * Customizable piece of the default paging text (defaults to "Refresh")
23360 refreshText : "Refresh",
23364 onRender : function(ct, position)
23366 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
23367 this.navgroup.parentId = this.id;
23368 this.navgroup.onRender(this.el, null);
23369 // add the buttons to the navgroup
23371 if(this.displayInfo){
23372 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
23373 this.displayEl = this.el.select('.x-paging-info', true).first();
23374 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
23375 // this.displayEl = navel.el.select('span',true).first();
23381 Roo.each(_this.buttons, function(e){ // this might need to use render????
23382 Roo.factory(e).onRender(_this.el, null);
23386 Roo.each(_this.toolbarItems, function(e) {
23387 _this.navgroup.addItem(e);
23391 this.first = this.navgroup.addItem({
23392 tooltip: this.firstText,
23394 icon : 'fa fa-backward',
23396 preventDefault: true,
23397 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23400 this.prev = this.navgroup.addItem({
23401 tooltip: this.prevText,
23403 icon : 'fa fa-step-backward',
23405 preventDefault: true,
23406 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
23408 //this.addSeparator();
23411 var field = this.navgroup.addItem( {
23413 cls : 'x-paging-position',
23415 html : this.beforePageText +
23416 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23417 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
23420 this.field = field.el.select('input', true).first();
23421 this.field.on("keydown", this.onPagingKeydown, this);
23422 this.field.on("focus", function(){this.dom.select();});
23425 this.afterTextEl = field.el.select('.x-paging-after',true).first();
23426 //this.field.setHeight(18);
23427 //this.addSeparator();
23428 this.next = this.navgroup.addItem({
23429 tooltip: this.nextText,
23431 html : ' <i class="fa fa-step-forward">',
23433 preventDefault: true,
23434 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
23436 this.last = this.navgroup.addItem({
23437 tooltip: this.lastText,
23438 icon : 'fa fa-forward',
23441 preventDefault: true,
23442 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
23444 //this.addSeparator();
23445 this.loading = this.navgroup.addItem({
23446 tooltip: this.refreshText,
23447 icon: 'fa fa-refresh',
23448 preventDefault: true,
23449 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23455 updateInfo : function(){
23456 if(this.displayEl){
23457 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23458 var msg = count == 0 ?
23462 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
23464 this.displayEl.update(msg);
23469 onLoad : function(ds, r, o){
23470 this.cursor = o.params ? o.params.start : 0;
23471 var d = this.getPageData(),
23475 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23476 this.field.dom.value = ap;
23477 this.first.setDisabled(ap == 1);
23478 this.prev.setDisabled(ap == 1);
23479 this.next.setDisabled(ap == ps);
23480 this.last.setDisabled(ap == ps);
23481 this.loading.enable();
23486 getPageData : function(){
23487 var total = this.ds.getTotalCount();
23490 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23491 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23496 onLoadError : function(){
23497 this.loading.enable();
23501 onPagingKeydown : function(e){
23502 var k = e.getKey();
23503 var d = this.getPageData();
23505 var v = this.field.dom.value, pageNum;
23506 if(!v || isNaN(pageNum = parseInt(v, 10))){
23507 this.field.dom.value = d.activePage;
23510 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23511 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23514 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))
23516 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23517 this.field.dom.value = pageNum;
23518 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23521 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23523 var v = this.field.dom.value, pageNum;
23524 var increment = (e.shiftKey) ? 10 : 1;
23525 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23528 if(!v || isNaN(pageNum = parseInt(v, 10))) {
23529 this.field.dom.value = d.activePage;
23532 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23534 this.field.dom.value = parseInt(v, 10) + increment;
23535 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23536 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23543 beforeLoad : function(){
23545 this.loading.disable();
23550 onClick : function(which){
23559 ds.load({params:{start: 0, limit: this.pageSize}});
23562 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23565 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23568 var total = ds.getTotalCount();
23569 var extra = total % this.pageSize;
23570 var lastStart = extra ? (total - extra) : total-this.pageSize;
23571 ds.load({params:{start: lastStart, limit: this.pageSize}});
23574 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23580 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23581 * @param {Roo.data.Store} store The data store to unbind
23583 unbind : function(ds){
23584 ds.un("beforeload", this.beforeLoad, this);
23585 ds.un("load", this.onLoad, this);
23586 ds.un("loadexception", this.onLoadError, this);
23587 ds.un("remove", this.updateInfo, this);
23588 ds.un("add", this.updateInfo, this);
23589 this.ds = undefined;
23593 * Binds the paging toolbar to the specified {@link Roo.data.Store}
23594 * @param {Roo.data.Store} store The data store to bind
23596 bind : function(ds){
23597 ds.on("beforeload", this.beforeLoad, this);
23598 ds.on("load", this.onLoad, this);
23599 ds.on("loadexception", this.onLoadError, this);
23600 ds.on("remove", this.updateInfo, this);
23601 ds.on("add", this.updateInfo, this);
23612 * @class Roo.bootstrap.MessageBar
23613 * @extends Roo.bootstrap.Component
23614 * Bootstrap MessageBar class
23615 * @cfg {String} html contents of the MessageBar
23616 * @cfg {String} weight (info | success | warning | danger) default info
23617 * @cfg {String} beforeClass insert the bar before the given class
23618 * @cfg {Boolean} closable (true | false) default false
23619 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23622 * Create a new Element
23623 * @param {Object} config The config object
23626 Roo.bootstrap.MessageBar = function(config){
23627 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23630 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
23636 beforeClass: 'bootstrap-sticky-wrap',
23638 getAutoCreate : function(){
23642 cls: 'alert alert-dismissable alert-' + this.weight,
23647 html: this.html || ''
23653 cfg.cls += ' alert-messages-fixed';
23667 onRender : function(ct, position)
23669 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23672 var cfg = Roo.apply({}, this.getAutoCreate());
23676 cfg.cls += ' ' + this.cls;
23679 cfg.style = this.style;
23681 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23683 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23686 this.el.select('>button.close').on('click', this.hide, this);
23692 if (!this.rendered) {
23698 this.fireEvent('show', this);
23704 if (!this.rendered) {
23710 this.fireEvent('hide', this);
23713 update : function()
23715 // var e = this.el.dom.firstChild;
23717 // if(this.closable){
23718 // e = e.nextSibling;
23721 // e.data = this.html || '';
23723 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23739 * @class Roo.bootstrap.Graph
23740 * @extends Roo.bootstrap.Component
23741 * Bootstrap Graph class
23745 @cfg {String} graphtype bar | vbar | pie
23746 @cfg {number} g_x coodinator | centre x (pie)
23747 @cfg {number} g_y coodinator | centre y (pie)
23748 @cfg {number} g_r radius (pie)
23749 @cfg {number} g_height height of the chart (respected by all elements in the set)
23750 @cfg {number} g_width width of the chart (respected by all elements in the set)
23751 @cfg {Object} title The title of the chart
23754 -opts (object) options for the chart
23756 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23757 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23759 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.
23760 o stacked (boolean) whether or not to tread values as in a stacked bar chart
23762 o stretch (boolean)
23764 -opts (object) options for the pie
23767 o startAngle (number)
23768 o endAngle (number)
23772 * Create a new Input
23773 * @param {Object} config The config object
23776 Roo.bootstrap.Graph = function(config){
23777 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23783 * The img click event for the img.
23784 * @param {Roo.EventObject} e
23790 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
23801 //g_colors: this.colors,
23808 getAutoCreate : function(){
23819 onRender : function(ct,position){
23822 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23824 if (typeof(Raphael) == 'undefined') {
23825 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23829 this.raphael = Raphael(this.el.dom);
23831 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23832 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23833 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23834 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23836 r.text(160, 10, "Single Series Chart").attr(txtattr);
23837 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23838 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23839 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23841 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23842 r.barchart(330, 10, 300, 220, data1);
23843 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23844 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23847 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23848 // r.barchart(30, 30, 560, 250, xdata, {
23849 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23850 // axis : "0 0 1 1",
23851 // axisxlabels : xdata
23852 // //yvalues : cols,
23855 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23857 // this.load(null,xdata,{
23858 // axis : "0 0 1 1",
23859 // axisxlabels : xdata
23864 load : function(graphtype,xdata,opts)
23866 this.raphael.clear();
23868 graphtype = this.graphtype;
23873 var r = this.raphael,
23874 fin = function () {
23875 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23877 fout = function () {
23878 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23880 pfin = function() {
23881 this.sector.stop();
23882 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23885 this.label[0].stop();
23886 this.label[0].attr({ r: 7.5 });
23887 this.label[1].attr({ "font-weight": 800 });
23890 pfout = function() {
23891 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23894 this.label[0].animate({ r: 5 }, 500, "bounce");
23895 this.label[1].attr({ "font-weight": 400 });
23901 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23904 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23907 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
23908 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23910 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23917 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23922 setTitle: function(o)
23927 initEvents: function() {
23930 this.el.on('click', this.onClick, this);
23934 onClick : function(e)
23936 Roo.log('img onclick');
23937 this.fireEvent('click', this, e);
23949 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23952 * @class Roo.bootstrap.dash.NumberBox
23953 * @extends Roo.bootstrap.Component
23954 * Bootstrap NumberBox class
23955 * @cfg {String} headline Box headline
23956 * @cfg {String} content Box content
23957 * @cfg {String} icon Box icon
23958 * @cfg {String} footer Footer text
23959 * @cfg {String} fhref Footer href
23962 * Create a new NumberBox
23963 * @param {Object} config The config object
23967 Roo.bootstrap.dash.NumberBox = function(config){
23968 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23972 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
23981 getAutoCreate : function(){
23985 cls : 'small-box ',
23993 cls : 'roo-headline',
23994 html : this.headline
23998 cls : 'roo-content',
23999 html : this.content
24013 cls : 'ion ' + this.icon
24022 cls : 'small-box-footer',
24023 href : this.fhref || '#',
24027 cfg.cn.push(footer);
24034 onRender : function(ct,position){
24035 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24042 setHeadline: function (value)
24044 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24047 setFooter: function (value, href)
24049 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24052 this.el.select('a.small-box-footer',true).first().attr('href', href);
24057 setContent: function (value)
24059 this.el.select('.roo-content',true).first().dom.innerHTML = value;
24062 initEvents: function()
24076 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24079 * @class Roo.bootstrap.dash.TabBox
24080 * @extends Roo.bootstrap.Component
24081 * Bootstrap TabBox class
24082 * @cfg {String} title Title of the TabBox
24083 * @cfg {String} icon Icon of the TabBox
24084 * @cfg {Boolean} showtabs (true|false) show the tabs default true
24085 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24088 * Create a new TabBox
24089 * @param {Object} config The config object
24093 Roo.bootstrap.dash.TabBox = function(config){
24094 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24099 * When a pane is added
24100 * @param {Roo.bootstrap.dash.TabPane} pane
24104 * @event activatepane
24105 * When a pane is activated
24106 * @param {Roo.bootstrap.dash.TabPane} pane
24108 "activatepane" : true
24116 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
24121 tabScrollable : false,
24123 getChildContainer : function()
24125 return this.el.select('.tab-content', true).first();
24128 getAutoCreate : function(){
24132 cls: 'pull-left header',
24140 cls: 'fa ' + this.icon
24146 cls: 'nav nav-tabs pull-right',
24152 if(this.tabScrollable){
24159 cls: 'nav nav-tabs pull-right',
24170 cls: 'nav-tabs-custom',
24175 cls: 'tab-content no-padding',
24183 initEvents : function()
24185 //Roo.log('add add pane handler');
24186 this.on('addpane', this.onAddPane, this);
24189 * Updates the box title
24190 * @param {String} html to set the title to.
24192 setTitle : function(value)
24194 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24196 onAddPane : function(pane)
24198 this.panes.push(pane);
24199 //Roo.log('addpane');
24201 // tabs are rendere left to right..
24202 if(!this.showtabs){
24206 var ctr = this.el.select('.nav-tabs', true).first();
24209 var existing = ctr.select('.nav-tab',true);
24210 var qty = existing.getCount();;
24213 var tab = ctr.createChild({
24215 cls : 'nav-tab' + (qty ? '' : ' active'),
24223 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24226 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24228 pane.el.addClass('active');
24233 onTabClick : function(ev,un,ob,pane)
24235 //Roo.log('tab - prev default');
24236 ev.preventDefault();
24239 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
24240 pane.tab.addClass('active');
24241 //Roo.log(pane.title);
24242 this.getChildContainer().select('.tab-pane',true).removeClass('active');
24243 // technically we should have a deactivate event.. but maybe add later.
24244 // and it should not de-activate the selected tab...
24245 this.fireEvent('activatepane', pane);
24246 pane.el.addClass('active');
24247 pane.fireEvent('activate');
24252 getActivePane : function()
24255 Roo.each(this.panes, function(p) {
24256 if(p.el.hasClass('active')){
24277 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24279 * @class Roo.bootstrap.TabPane
24280 * @extends Roo.bootstrap.Component
24281 * Bootstrap TabPane class
24282 * @cfg {Boolean} active (false | true) Default false
24283 * @cfg {String} title title of panel
24287 * Create a new TabPane
24288 * @param {Object} config The config object
24291 Roo.bootstrap.dash.TabPane = function(config){
24292 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
24298 * When a pane is activated
24299 * @param {Roo.bootstrap.dash.TabPane} pane
24306 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
24311 // the tabBox that this is attached to.
24314 getAutoCreate : function()
24322 cfg.cls += ' active';
24327 initEvents : function()
24329 //Roo.log('trigger add pane handler');
24330 this.parent().fireEvent('addpane', this)
24334 * Updates the tab title
24335 * @param {String} html to set the title to.
24337 setTitle: function(str)
24343 this.tab.select('a', true).first().dom.innerHTML = str;
24360 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24363 * @class Roo.bootstrap.menu.Menu
24364 * @extends Roo.bootstrap.Component
24365 * Bootstrap Menu class - container for Menu
24366 * @cfg {String} html Text of the menu
24367 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
24368 * @cfg {String} icon Font awesome icon
24369 * @cfg {String} pos Menu align to (top | bottom) default bottom
24373 * Create a new Menu
24374 * @param {Object} config The config object
24378 Roo.bootstrap.menu.Menu = function(config){
24379 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
24383 * @event beforeshow
24384 * Fires before this menu is displayed
24385 * @param {Roo.bootstrap.menu.Menu} this
24389 * @event beforehide
24390 * Fires before this menu is hidden
24391 * @param {Roo.bootstrap.menu.Menu} this
24396 * Fires after this menu is displayed
24397 * @param {Roo.bootstrap.menu.Menu} this
24402 * Fires after this menu is hidden
24403 * @param {Roo.bootstrap.menu.Menu} this
24408 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24409 * @param {Roo.bootstrap.menu.Menu} this
24410 * @param {Roo.EventObject} e
24417 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
24421 weight : 'default',
24426 getChildContainer : function() {
24427 if(this.isSubMenu){
24431 return this.el.select('ul.dropdown-menu', true).first();
24434 getAutoCreate : function()
24439 cls : 'roo-menu-text',
24447 cls : 'fa ' + this.icon
24458 cls : 'dropdown-button btn btn-' + this.weight,
24463 cls : 'dropdown-toggle btn btn-' + this.weight,
24473 cls : 'dropdown-menu'
24479 if(this.pos == 'top'){
24480 cfg.cls += ' dropup';
24483 if(this.isSubMenu){
24486 cls : 'dropdown-menu'
24493 onRender : function(ct, position)
24495 this.isSubMenu = ct.hasClass('dropdown-submenu');
24497 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24500 initEvents : function()
24502 if(this.isSubMenu){
24506 this.hidden = true;
24508 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24509 this.triggerEl.on('click', this.onTriggerPress, this);
24511 this.buttonEl = this.el.select('button.dropdown-button', true).first();
24512 this.buttonEl.on('click', this.onClick, this);
24518 if(this.isSubMenu){
24522 return this.el.select('ul.dropdown-menu', true).first();
24525 onClick : function(e)
24527 this.fireEvent("click", this, e);
24530 onTriggerPress : function(e)
24532 if (this.isVisible()) {
24539 isVisible : function(){
24540 return !this.hidden;
24545 this.fireEvent("beforeshow", this);
24547 this.hidden = false;
24548 this.el.addClass('open');
24550 Roo.get(document).on("mouseup", this.onMouseUp, this);
24552 this.fireEvent("show", this);
24559 this.fireEvent("beforehide", this);
24561 this.hidden = true;
24562 this.el.removeClass('open');
24564 Roo.get(document).un("mouseup", this.onMouseUp);
24566 this.fireEvent("hide", this);
24569 onMouseUp : function()
24583 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24586 * @class Roo.bootstrap.menu.Item
24587 * @extends Roo.bootstrap.Component
24588 * Bootstrap MenuItem class
24589 * @cfg {Boolean} submenu (true | false) default false
24590 * @cfg {String} html text of the item
24591 * @cfg {String} href the link
24592 * @cfg {Boolean} disable (true | false) default false
24593 * @cfg {Boolean} preventDefault (true | false) default true
24594 * @cfg {String} icon Font awesome icon
24595 * @cfg {String} pos Submenu align to (left | right) default right
24599 * Create a new Item
24600 * @param {Object} config The config object
24604 Roo.bootstrap.menu.Item = function(config){
24605 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24609 * Fires when the mouse is hovering over this menu
24610 * @param {Roo.bootstrap.menu.Item} this
24611 * @param {Roo.EventObject} e
24616 * Fires when the mouse exits this menu
24617 * @param {Roo.bootstrap.menu.Item} this
24618 * @param {Roo.EventObject} e
24624 * The raw click event for the entire grid.
24625 * @param {Roo.EventObject} e
24631 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
24636 preventDefault: true,
24641 getAutoCreate : function()
24646 cls : 'roo-menu-item-text',
24654 cls : 'fa ' + this.icon
24663 href : this.href || '#',
24670 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24674 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24676 if(this.pos == 'left'){
24677 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24684 initEvents : function()
24686 this.el.on('mouseover', this.onMouseOver, this);
24687 this.el.on('mouseout', this.onMouseOut, this);
24689 this.el.select('a', true).first().on('click', this.onClick, this);
24693 onClick : function(e)
24695 if(this.preventDefault){
24696 e.preventDefault();
24699 this.fireEvent("click", this, e);
24702 onMouseOver : function(e)
24704 if(this.submenu && this.pos == 'left'){
24705 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24708 this.fireEvent("mouseover", this, e);
24711 onMouseOut : function(e)
24713 this.fireEvent("mouseout", this, e);
24725 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24728 * @class Roo.bootstrap.menu.Separator
24729 * @extends Roo.bootstrap.Component
24730 * Bootstrap Separator class
24733 * Create a new Separator
24734 * @param {Object} config The config object
24738 Roo.bootstrap.menu.Separator = function(config){
24739 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24742 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
24744 getAutoCreate : function(){
24765 * @class Roo.bootstrap.Tooltip
24766 * Bootstrap Tooltip class
24767 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24768 * to determine which dom element triggers the tooltip.
24770 * It needs to add support for additional attributes like tooltip-position
24773 * Create a new Toolti
24774 * @param {Object} config The config object
24777 Roo.bootstrap.Tooltip = function(config){
24778 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24781 Roo.apply(Roo.bootstrap.Tooltip, {
24783 * @function init initialize tooltip monitoring.
24787 currentTip : false,
24788 currentRegion : false,
24794 Roo.get(document).on('mouseover', this.enter ,this);
24795 Roo.get(document).on('mouseout', this.leave, this);
24798 this.currentTip = new Roo.bootstrap.Tooltip();
24801 enter : function(ev)
24803 var dom = ev.getTarget();
24805 //Roo.log(['enter',dom]);
24806 var el = Roo.fly(dom);
24807 if (this.currentEl) {
24809 //Roo.log(this.currentEl);
24810 //Roo.log(this.currentEl.contains(dom));
24811 if (this.currentEl == el) {
24814 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24820 if (this.currentTip.el) {
24821 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24825 if(!el || el.dom == document){
24831 // you can not look for children, as if el is the body.. then everythign is the child..
24832 if (!el.attr('tooltip')) { //
24833 if (!el.select("[tooltip]").elements.length) {
24836 // is the mouse over this child...?
24837 bindEl = el.select("[tooltip]").first();
24838 var xy = ev.getXY();
24839 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24840 //Roo.log("not in region.");
24843 //Roo.log("child element over..");
24846 this.currentEl = bindEl;
24847 this.currentTip.bind(bindEl);
24848 this.currentRegion = Roo.lib.Region.getRegion(dom);
24849 this.currentTip.enter();
24852 leave : function(ev)
24854 var dom = ev.getTarget();
24855 //Roo.log(['leave',dom]);
24856 if (!this.currentEl) {
24861 if (dom != this.currentEl.dom) {
24864 var xy = ev.getXY();
24865 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
24868 // only activate leave if mouse cursor is outside... bounding box..
24873 if (this.currentTip) {
24874 this.currentTip.leave();
24876 //Roo.log('clear currentEl');
24877 this.currentEl = false;
24882 'left' : ['r-l', [-2,0], 'right'],
24883 'right' : ['l-r', [2,0], 'left'],
24884 'bottom' : ['t-b', [0,2], 'top'],
24885 'top' : [ 'b-t', [0,-2], 'bottom']
24891 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
24896 delay : null, // can be { show : 300 , hide: 500}
24900 hoverState : null, //???
24902 placement : 'bottom',
24904 getAutoCreate : function(){
24911 cls : 'tooltip-arrow'
24914 cls : 'tooltip-inner'
24921 bind : function(el)
24927 enter : function () {
24929 if (this.timeout != null) {
24930 clearTimeout(this.timeout);
24933 this.hoverState = 'in';
24934 //Roo.log("enter - show");
24935 if (!this.delay || !this.delay.show) {
24940 this.timeout = setTimeout(function () {
24941 if (_t.hoverState == 'in') {
24944 }, this.delay.show);
24948 clearTimeout(this.timeout);
24950 this.hoverState = 'out';
24951 if (!this.delay || !this.delay.hide) {
24957 this.timeout = setTimeout(function () {
24958 //Roo.log("leave - timeout");
24960 if (_t.hoverState == 'out') {
24962 Roo.bootstrap.Tooltip.currentEl = false;
24970 this.render(document.body);
24973 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24975 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24977 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24979 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24981 var placement = typeof this.placement == 'function' ?
24982 this.placement.call(this, this.el, on_el) :
24985 var autoToken = /\s?auto?\s?/i;
24986 var autoPlace = autoToken.test(placement);
24988 placement = placement.replace(autoToken, '') || 'top';
24992 //this.el.setXY([0,0]);
24994 //this.el.dom.style.display='block';
24996 //this.el.appendTo(on_el);
24998 var p = this.getPosition();
24999 var box = this.el.getBox();
25005 var align = Roo.bootstrap.Tooltip.alignment[placement];
25007 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25009 if(placement == 'top' || placement == 'bottom'){
25011 placement = 'right';
25014 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25015 placement = 'left';
25018 var scroll = Roo.select('body', true).first().getScroll();
25020 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25026 align = Roo.bootstrap.Tooltip.alignment[placement];
25028 this.el.alignTo(this.bindEl, align[0],align[1]);
25029 //var arrow = this.el.select('.arrow',true).first();
25030 //arrow.set(align[2],
25032 this.el.addClass(placement);
25034 this.el.addClass('in fade');
25036 this.hoverState = null;
25038 if (this.el.hasClass('fade')) {
25049 //this.el.setXY([0,0]);
25050 this.el.removeClass('in');
25066 * @class Roo.bootstrap.LocationPicker
25067 * @extends Roo.bootstrap.Component
25068 * Bootstrap LocationPicker class
25069 * @cfg {Number} latitude Position when init default 0
25070 * @cfg {Number} longitude Position when init default 0
25071 * @cfg {Number} zoom default 15
25072 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25073 * @cfg {Boolean} mapTypeControl default false
25074 * @cfg {Boolean} disableDoubleClickZoom default false
25075 * @cfg {Boolean} scrollwheel default true
25076 * @cfg {Boolean} streetViewControl default false
25077 * @cfg {Number} radius default 0
25078 * @cfg {String} locationName
25079 * @cfg {Boolean} draggable default true
25080 * @cfg {Boolean} enableAutocomplete default false
25081 * @cfg {Boolean} enableReverseGeocode default true
25082 * @cfg {String} markerTitle
25085 * Create a new LocationPicker
25086 * @param {Object} config The config object
25090 Roo.bootstrap.LocationPicker = function(config){
25092 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25097 * Fires when the picker initialized.
25098 * @param {Roo.bootstrap.LocationPicker} this
25099 * @param {Google Location} location
25103 * @event positionchanged
25104 * Fires when the picker position changed.
25105 * @param {Roo.bootstrap.LocationPicker} this
25106 * @param {Google Location} location
25108 positionchanged : true,
25111 * Fires when the map resize.
25112 * @param {Roo.bootstrap.LocationPicker} this
25117 * Fires when the map show.
25118 * @param {Roo.bootstrap.LocationPicker} this
25123 * Fires when the map hide.
25124 * @param {Roo.bootstrap.LocationPicker} this
25129 * Fires when click the map.
25130 * @param {Roo.bootstrap.LocationPicker} this
25131 * @param {Map event} e
25135 * @event mapRightClick
25136 * Fires when right click the map.
25137 * @param {Roo.bootstrap.LocationPicker} this
25138 * @param {Map event} e
25140 mapRightClick : true,
25142 * @event markerClick
25143 * Fires when click the marker.
25144 * @param {Roo.bootstrap.LocationPicker} this
25145 * @param {Map event} e
25147 markerClick : true,
25149 * @event markerRightClick
25150 * Fires when right click the marker.
25151 * @param {Roo.bootstrap.LocationPicker} this
25152 * @param {Map event} e
25154 markerRightClick : true,
25156 * @event OverlayViewDraw
25157 * Fires when OverlayView Draw
25158 * @param {Roo.bootstrap.LocationPicker} this
25160 OverlayViewDraw : true,
25162 * @event OverlayViewOnAdd
25163 * Fires when OverlayView Draw
25164 * @param {Roo.bootstrap.LocationPicker} this
25166 OverlayViewOnAdd : true,
25168 * @event OverlayViewOnRemove
25169 * Fires when OverlayView Draw
25170 * @param {Roo.bootstrap.LocationPicker} this
25172 OverlayViewOnRemove : true,
25174 * @event OverlayViewShow
25175 * Fires when OverlayView Draw
25176 * @param {Roo.bootstrap.LocationPicker} this
25177 * @param {Pixel} cpx
25179 OverlayViewShow : true,
25181 * @event OverlayViewHide
25182 * Fires when OverlayView Draw
25183 * @param {Roo.bootstrap.LocationPicker} this
25185 OverlayViewHide : true,
25187 * @event loadexception
25188 * Fires when load google lib failed.
25189 * @param {Roo.bootstrap.LocationPicker} this
25191 loadexception : true
25196 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
25198 gMapContext: false,
25204 mapTypeControl: false,
25205 disableDoubleClickZoom: false,
25207 streetViewControl: false,
25211 enableAutocomplete: false,
25212 enableReverseGeocode: true,
25215 getAutoCreate: function()
25220 cls: 'roo-location-picker'
25226 initEvents: function(ct, position)
25228 if(!this.el.getWidth() || this.isApplied()){
25232 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25237 initial: function()
25239 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
25240 this.fireEvent('loadexception', this);
25244 if(!this.mapTypeId){
25245 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
25248 this.gMapContext = this.GMapContext();
25250 this.initOverlayView();
25252 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
25256 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
25257 _this.setPosition(_this.gMapContext.marker.position);
25260 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
25261 _this.fireEvent('mapClick', this, event);
25265 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
25266 _this.fireEvent('mapRightClick', this, event);
25270 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
25271 _this.fireEvent('markerClick', this, event);
25275 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
25276 _this.fireEvent('markerRightClick', this, event);
25280 this.setPosition(this.gMapContext.location);
25282 this.fireEvent('initial', this, this.gMapContext.location);
25285 initOverlayView: function()
25289 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
25293 _this.fireEvent('OverlayViewDraw', _this);
25298 _this.fireEvent('OverlayViewOnAdd', _this);
25301 onRemove: function()
25303 _this.fireEvent('OverlayViewOnRemove', _this);
25306 show: function(cpx)
25308 _this.fireEvent('OverlayViewShow', _this, cpx);
25313 _this.fireEvent('OverlayViewHide', _this);
25319 fromLatLngToContainerPixel: function(event)
25321 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
25324 isApplied: function()
25326 return this.getGmapContext() == false ? false : true;
25329 getGmapContext: function()
25331 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
25334 GMapContext: function()
25336 var position = new google.maps.LatLng(this.latitude, this.longitude);
25338 var _map = new google.maps.Map(this.el.dom, {
25341 mapTypeId: this.mapTypeId,
25342 mapTypeControl: this.mapTypeControl,
25343 disableDoubleClickZoom: this.disableDoubleClickZoom,
25344 scrollwheel: this.scrollwheel,
25345 streetViewControl: this.streetViewControl,
25346 locationName: this.locationName,
25347 draggable: this.draggable,
25348 enableAutocomplete: this.enableAutocomplete,
25349 enableReverseGeocode: this.enableReverseGeocode
25352 var _marker = new google.maps.Marker({
25353 position: position,
25355 title: this.markerTitle,
25356 draggable: this.draggable
25363 location: position,
25364 radius: this.radius,
25365 locationName: this.locationName,
25366 addressComponents: {
25367 formatted_address: null,
25368 addressLine1: null,
25369 addressLine2: null,
25371 streetNumber: null,
25375 stateOrProvince: null
25378 domContainer: this.el.dom,
25379 geodecoder: new google.maps.Geocoder()
25383 drawCircle: function(center, radius, options)
25385 if (this.gMapContext.circle != null) {
25386 this.gMapContext.circle.setMap(null);
25390 options = Roo.apply({}, options, {
25391 strokeColor: "#0000FF",
25392 strokeOpacity: .35,
25394 fillColor: "#0000FF",
25398 options.map = this.gMapContext.map;
25399 options.radius = radius;
25400 options.center = center;
25401 this.gMapContext.circle = new google.maps.Circle(options);
25402 return this.gMapContext.circle;
25408 setPosition: function(location)
25410 this.gMapContext.location = location;
25411 this.gMapContext.marker.setPosition(location);
25412 this.gMapContext.map.panTo(location);
25413 this.drawCircle(location, this.gMapContext.radius, {});
25417 if (this.gMapContext.settings.enableReverseGeocode) {
25418 this.gMapContext.geodecoder.geocode({
25419 latLng: this.gMapContext.location
25420 }, function(results, status) {
25422 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25423 _this.gMapContext.locationName = results[0].formatted_address;
25424 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25426 _this.fireEvent('positionchanged', this, location);
25433 this.fireEvent('positionchanged', this, location);
25438 google.maps.event.trigger(this.gMapContext.map, "resize");
25440 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25442 this.fireEvent('resize', this);
25445 setPositionByLatLng: function(latitude, longitude)
25447 this.setPosition(new google.maps.LatLng(latitude, longitude));
25450 getCurrentPosition: function()
25453 latitude: this.gMapContext.location.lat(),
25454 longitude: this.gMapContext.location.lng()
25458 getAddressName: function()
25460 return this.gMapContext.locationName;
25463 getAddressComponents: function()
25465 return this.gMapContext.addressComponents;
25468 address_component_from_google_geocode: function(address_components)
25472 for (var i = 0; i < address_components.length; i++) {
25473 var component = address_components[i];
25474 if (component.types.indexOf("postal_code") >= 0) {
25475 result.postalCode = component.short_name;
25476 } else if (component.types.indexOf("street_number") >= 0) {
25477 result.streetNumber = component.short_name;
25478 } else if (component.types.indexOf("route") >= 0) {
25479 result.streetName = component.short_name;
25480 } else if (component.types.indexOf("neighborhood") >= 0) {
25481 result.city = component.short_name;
25482 } else if (component.types.indexOf("locality") >= 0) {
25483 result.city = component.short_name;
25484 } else if (component.types.indexOf("sublocality") >= 0) {
25485 result.district = component.short_name;
25486 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25487 result.stateOrProvince = component.short_name;
25488 } else if (component.types.indexOf("country") >= 0) {
25489 result.country = component.short_name;
25493 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25494 result.addressLine2 = "";
25498 setZoomLevel: function(zoom)
25500 this.gMapContext.map.setZoom(zoom);
25513 this.fireEvent('show', this);
25524 this.fireEvent('hide', this);
25529 Roo.apply(Roo.bootstrap.LocationPicker, {
25531 OverlayView : function(map, options)
25533 options = options || {};
25547 * @class Roo.bootstrap.Alert
25548 * @extends Roo.bootstrap.Component
25549 * Bootstrap Alert class
25550 * @cfg {String} title The title of alert
25551 * @cfg {String} html The content of alert
25552 * @cfg {String} weight ( success | info | warning | danger )
25553 * @cfg {String} faicon font-awesomeicon
25556 * Create a new alert
25557 * @param {Object} config The config object
25561 Roo.bootstrap.Alert = function(config){
25562 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25566 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
25573 getAutoCreate : function()
25582 cls : 'roo-alert-icon'
25587 cls : 'roo-alert-title',
25592 cls : 'roo-alert-text',
25599 cfg.cn[0].cls += ' fa ' + this.faicon;
25603 cfg.cls += ' alert-' + this.weight;
25609 initEvents: function()
25611 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25614 setTitle : function(str)
25616 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25619 setText : function(str)
25621 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25624 setWeight : function(weight)
25627 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25630 this.weight = weight;
25632 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25635 setIcon : function(icon)
25638 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25641 this.faicon = icon;
25643 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25664 * @class Roo.bootstrap.UploadCropbox
25665 * @extends Roo.bootstrap.Component
25666 * Bootstrap UploadCropbox class
25667 * @cfg {String} emptyText show when image has been loaded
25668 * @cfg {String} rotateNotify show when image too small to rotate
25669 * @cfg {Number} errorTimeout default 3000
25670 * @cfg {Number} minWidth default 300
25671 * @cfg {Number} minHeight default 300
25672 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25673 * @cfg {Boolean} isDocument (true|false) default false
25674 * @cfg {String} url action url
25675 * @cfg {String} paramName default 'imageUpload'
25676 * @cfg {String} method default POST
25677 * @cfg {Boolean} loadMask (true|false) default true
25678 * @cfg {Boolean} loadingText default 'Loading...'
25681 * Create a new UploadCropbox
25682 * @param {Object} config The config object
25685 Roo.bootstrap.UploadCropbox = function(config){
25686 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25690 * @event beforeselectfile
25691 * Fire before select file
25692 * @param {Roo.bootstrap.UploadCropbox} this
25694 "beforeselectfile" : true,
25697 * Fire after initEvent
25698 * @param {Roo.bootstrap.UploadCropbox} this
25703 * Fire after initEvent
25704 * @param {Roo.bootstrap.UploadCropbox} this
25705 * @param {String} data
25710 * Fire when preparing the file data
25711 * @param {Roo.bootstrap.UploadCropbox} this
25712 * @param {Object} file
25717 * Fire when get exception
25718 * @param {Roo.bootstrap.UploadCropbox} this
25719 * @param {XMLHttpRequest} xhr
25721 "exception" : true,
25723 * @event beforeloadcanvas
25724 * Fire before load the canvas
25725 * @param {Roo.bootstrap.UploadCropbox} this
25726 * @param {String} src
25728 "beforeloadcanvas" : true,
25731 * Fire when trash image
25732 * @param {Roo.bootstrap.UploadCropbox} this
25737 * Fire when download the image
25738 * @param {Roo.bootstrap.UploadCropbox} this
25742 * @event footerbuttonclick
25743 * Fire when footerbuttonclick
25744 * @param {Roo.bootstrap.UploadCropbox} this
25745 * @param {String} type
25747 "footerbuttonclick" : true,
25751 * @param {Roo.bootstrap.UploadCropbox} this
25756 * Fire when rotate the image
25757 * @param {Roo.bootstrap.UploadCropbox} this
25758 * @param {String} pos
25763 * Fire when inspect the file
25764 * @param {Roo.bootstrap.UploadCropbox} this
25765 * @param {Object} file
25770 * Fire when xhr upload the file
25771 * @param {Roo.bootstrap.UploadCropbox} this
25772 * @param {Object} data
25777 * Fire when arrange the file data
25778 * @param {Roo.bootstrap.UploadCropbox} this
25779 * @param {Object} formData
25784 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25787 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
25789 emptyText : 'Click to upload image',
25790 rotateNotify : 'Image is too small to rotate',
25791 errorTimeout : 3000,
25805 cropType : 'image/jpeg',
25807 canvasLoaded : false,
25808 isDocument : false,
25810 paramName : 'imageUpload',
25812 loadingText : 'Loading...',
25815 getAutoCreate : function()
25819 cls : 'roo-upload-cropbox',
25823 cls : 'roo-upload-cropbox-selector',
25828 cls : 'roo-upload-cropbox-body',
25829 style : 'cursor:pointer',
25833 cls : 'roo-upload-cropbox-preview'
25837 cls : 'roo-upload-cropbox-thumb'
25841 cls : 'roo-upload-cropbox-empty-notify',
25842 html : this.emptyText
25846 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25847 html : this.rotateNotify
25853 cls : 'roo-upload-cropbox-footer',
25856 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25866 onRender : function(ct, position)
25868 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25870 if (this.buttons.length) {
25872 Roo.each(this.buttons, function(bb) {
25874 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25876 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25882 this.maskEl = this.el;
25886 initEvents : function()
25888 this.urlAPI = (window.createObjectURL && window) ||
25889 (window.URL && URL.revokeObjectURL && URL) ||
25890 (window.webkitURL && webkitURL);
25892 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25893 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25895 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25896 this.selectorEl.hide();
25898 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25899 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25901 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25902 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25903 this.thumbEl.hide();
25905 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25906 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25908 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25909 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25910 this.errorEl.hide();
25912 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25913 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25914 this.footerEl.hide();
25916 this.setThumbBoxSize();
25922 this.fireEvent('initial', this);
25929 window.addEventListener("resize", function() { _this.resize(); } );
25931 this.bodyEl.on('click', this.beforeSelectFile, this);
25934 this.bodyEl.on('touchstart', this.onTouchStart, this);
25935 this.bodyEl.on('touchmove', this.onTouchMove, this);
25936 this.bodyEl.on('touchend', this.onTouchEnd, this);
25940 this.bodyEl.on('mousedown', this.onMouseDown, this);
25941 this.bodyEl.on('mousemove', this.onMouseMove, this);
25942 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25943 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25944 Roo.get(document).on('mouseup', this.onMouseUp, this);
25947 this.selectorEl.on('change', this.onFileSelected, this);
25953 this.baseScale = 1;
25955 this.baseRotate = 1;
25956 this.dragable = false;
25957 this.pinching = false;
25960 this.cropData = false;
25961 this.notifyEl.dom.innerHTML = this.emptyText;
25963 this.selectorEl.dom.value = '';
25967 resize : function()
25969 if(this.fireEvent('resize', this) != false){
25970 this.setThumbBoxPosition();
25971 this.setCanvasPosition();
25975 onFooterButtonClick : function(e, el, o, type)
25978 case 'rotate-left' :
25979 this.onRotateLeft(e);
25981 case 'rotate-right' :
25982 this.onRotateRight(e);
25985 this.beforeSelectFile(e);
26000 this.fireEvent('footerbuttonclick', this, type);
26003 beforeSelectFile : function(e)
26005 e.preventDefault();
26007 if(this.fireEvent('beforeselectfile', this) != false){
26008 this.selectorEl.dom.click();
26012 onFileSelected : function(e)
26014 e.preventDefault();
26016 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26020 var file = this.selectorEl.dom.files[0];
26022 if(this.fireEvent('inspect', this, file) != false){
26023 this.prepare(file);
26028 trash : function(e)
26030 this.fireEvent('trash', this);
26033 download : function(e)
26035 this.fireEvent('download', this);
26038 loadCanvas : function(src)
26040 if(this.fireEvent('beforeloadcanvas', this, src) != false){
26044 this.imageEl = document.createElement('img');
26048 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26050 this.imageEl.src = src;
26054 onLoadCanvas : function()
26056 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26057 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26059 this.bodyEl.un('click', this.beforeSelectFile, this);
26061 this.notifyEl.hide();
26062 this.thumbEl.show();
26063 this.footerEl.show();
26065 this.baseRotateLevel();
26067 if(this.isDocument){
26068 this.setThumbBoxSize();
26071 this.setThumbBoxPosition();
26073 this.baseScaleLevel();
26079 this.canvasLoaded = true;
26082 this.maskEl.unmask();
26087 setCanvasPosition : function()
26089 if(!this.canvasEl){
26093 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26094 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26096 this.previewEl.setLeft(pw);
26097 this.previewEl.setTop(ph);
26101 onMouseDown : function(e)
26105 this.dragable = true;
26106 this.pinching = false;
26108 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26109 this.dragable = false;
26113 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26114 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26118 onMouseMove : function(e)
26122 if(!this.canvasLoaded){
26126 if (!this.dragable){
26130 var minX = Math.ceil(this.thumbEl.getLeft(true));
26131 var minY = Math.ceil(this.thumbEl.getTop(true));
26133 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26134 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26136 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26137 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26139 x = x - this.mouseX;
26140 y = y - this.mouseY;
26142 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26143 var bgY = Math.ceil(y + this.previewEl.getTop(true));
26145 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26146 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26148 this.previewEl.setLeft(bgX);
26149 this.previewEl.setTop(bgY);
26151 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26152 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26155 onMouseUp : function(e)
26159 this.dragable = false;
26162 onMouseWheel : function(e)
26166 this.startScale = this.scale;
26168 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26170 if(!this.zoomable()){
26171 this.scale = this.startScale;
26180 zoomable : function()
26182 var minScale = this.thumbEl.getWidth() / this.minWidth;
26184 if(this.minWidth < this.minHeight){
26185 minScale = this.thumbEl.getHeight() / this.minHeight;
26188 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26189 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26193 (this.rotate == 0 || this.rotate == 180) &&
26195 width > this.imageEl.OriginWidth ||
26196 height > this.imageEl.OriginHeight ||
26197 (width < this.minWidth && height < this.minHeight)
26205 (this.rotate == 90 || this.rotate == 270) &&
26207 width > this.imageEl.OriginWidth ||
26208 height > this.imageEl.OriginHeight ||
26209 (width < this.minHeight && height < this.minWidth)
26216 !this.isDocument &&
26217 (this.rotate == 0 || this.rotate == 180) &&
26219 width < this.minWidth ||
26220 width > this.imageEl.OriginWidth ||
26221 height < this.minHeight ||
26222 height > this.imageEl.OriginHeight
26229 !this.isDocument &&
26230 (this.rotate == 90 || this.rotate == 270) &&
26232 width < this.minHeight ||
26233 width > this.imageEl.OriginWidth ||
26234 height < this.minWidth ||
26235 height > this.imageEl.OriginHeight
26245 onRotateLeft : function(e)
26247 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26249 var minScale = this.thumbEl.getWidth() / this.minWidth;
26251 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26252 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26254 this.startScale = this.scale;
26256 while (this.getScaleLevel() < minScale){
26258 this.scale = this.scale + 1;
26260 if(!this.zoomable()){
26265 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26266 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26271 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26278 this.scale = this.startScale;
26280 this.onRotateFail();
26285 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26287 if(this.isDocument){
26288 this.setThumbBoxSize();
26289 this.setThumbBoxPosition();
26290 this.setCanvasPosition();
26295 this.fireEvent('rotate', this, 'left');
26299 onRotateRight : function(e)
26301 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26303 var minScale = this.thumbEl.getWidth() / this.minWidth;
26305 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26306 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26308 this.startScale = this.scale;
26310 while (this.getScaleLevel() < minScale){
26312 this.scale = this.scale + 1;
26314 if(!this.zoomable()){
26319 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26320 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26325 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26332 this.scale = this.startScale;
26334 this.onRotateFail();
26339 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26341 if(this.isDocument){
26342 this.setThumbBoxSize();
26343 this.setThumbBoxPosition();
26344 this.setCanvasPosition();
26349 this.fireEvent('rotate', this, 'right');
26352 onRotateFail : function()
26354 this.errorEl.show(true);
26358 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26363 this.previewEl.dom.innerHTML = '';
26365 var canvasEl = document.createElement("canvas");
26367 var contextEl = canvasEl.getContext("2d");
26369 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26370 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26371 var center = this.imageEl.OriginWidth / 2;
26373 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26374 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26375 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26376 center = this.imageEl.OriginHeight / 2;
26379 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26381 contextEl.translate(center, center);
26382 contextEl.rotate(this.rotate * Math.PI / 180);
26384 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26386 this.canvasEl = document.createElement("canvas");
26388 this.contextEl = this.canvasEl.getContext("2d");
26390 switch (this.rotate) {
26393 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26394 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26396 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26401 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26402 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26404 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26405 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);
26409 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26414 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26415 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26417 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26418 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);
26422 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);
26427 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26428 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26430 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26431 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26435 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);
26442 this.previewEl.appendChild(this.canvasEl);
26444 this.setCanvasPosition();
26449 if(!this.canvasLoaded){
26453 var imageCanvas = document.createElement("canvas");
26455 var imageContext = imageCanvas.getContext("2d");
26457 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26458 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26460 var center = imageCanvas.width / 2;
26462 imageContext.translate(center, center);
26464 imageContext.rotate(this.rotate * Math.PI / 180);
26466 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26468 var canvas = document.createElement("canvas");
26470 var context = canvas.getContext("2d");
26472 canvas.width = this.minWidth;
26473 canvas.height = this.minHeight;
26475 switch (this.rotate) {
26478 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26479 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26481 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26482 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26484 var targetWidth = this.minWidth - 2 * x;
26485 var targetHeight = this.minHeight - 2 * y;
26489 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26490 scale = targetWidth / width;
26493 if(x > 0 && y == 0){
26494 scale = targetHeight / height;
26497 if(x > 0 && y > 0){
26498 scale = targetWidth / width;
26500 if(width < height){
26501 scale = targetHeight / height;
26505 context.scale(scale, scale);
26507 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26508 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26510 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26511 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26513 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26518 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26519 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26521 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26522 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26524 var targetWidth = this.minWidth - 2 * x;
26525 var targetHeight = this.minHeight - 2 * y;
26529 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26530 scale = targetWidth / width;
26533 if(x > 0 && y == 0){
26534 scale = targetHeight / height;
26537 if(x > 0 && y > 0){
26538 scale = targetWidth / width;
26540 if(width < height){
26541 scale = targetHeight / height;
26545 context.scale(scale, scale);
26547 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26548 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26550 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26551 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26553 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26555 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26560 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26561 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26563 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26564 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26566 var targetWidth = this.minWidth - 2 * x;
26567 var targetHeight = this.minHeight - 2 * y;
26571 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26572 scale = targetWidth / width;
26575 if(x > 0 && y == 0){
26576 scale = targetHeight / height;
26579 if(x > 0 && y > 0){
26580 scale = targetWidth / width;
26582 if(width < height){
26583 scale = targetHeight / height;
26587 context.scale(scale, scale);
26589 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26590 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26592 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26593 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26595 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26596 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26598 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26603 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26604 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26606 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26607 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26609 var targetWidth = this.minWidth - 2 * x;
26610 var targetHeight = this.minHeight - 2 * y;
26614 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26615 scale = targetWidth / width;
26618 if(x > 0 && y == 0){
26619 scale = targetHeight / height;
26622 if(x > 0 && y > 0){
26623 scale = targetWidth / width;
26625 if(width < height){
26626 scale = targetHeight / height;
26630 context.scale(scale, scale);
26632 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26633 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26635 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26636 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26638 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26640 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26647 this.cropData = canvas.toDataURL(this.cropType);
26649 if(this.fireEvent('crop', this, this.cropData) !== false){
26650 this.process(this.file, this.cropData);
26657 setThumbBoxSize : function()
26661 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26662 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26663 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26665 this.minWidth = width;
26666 this.minHeight = height;
26668 if(this.rotate == 90 || this.rotate == 270){
26669 this.minWidth = height;
26670 this.minHeight = width;
26675 width = Math.ceil(this.minWidth * height / this.minHeight);
26677 if(this.minWidth > this.minHeight){
26679 height = Math.ceil(this.minHeight * width / this.minWidth);
26682 this.thumbEl.setStyle({
26683 width : width + 'px',
26684 height : height + 'px'
26691 setThumbBoxPosition : function()
26693 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26694 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26696 this.thumbEl.setLeft(x);
26697 this.thumbEl.setTop(y);
26701 baseRotateLevel : function()
26703 this.baseRotate = 1;
26706 typeof(this.exif) != 'undefined' &&
26707 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26708 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26710 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26713 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26717 baseScaleLevel : function()
26721 if(this.isDocument){
26723 if(this.baseRotate == 6 || this.baseRotate == 8){
26725 height = this.thumbEl.getHeight();
26726 this.baseScale = height / this.imageEl.OriginWidth;
26728 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26729 width = this.thumbEl.getWidth();
26730 this.baseScale = width / this.imageEl.OriginHeight;
26736 height = this.thumbEl.getHeight();
26737 this.baseScale = height / this.imageEl.OriginHeight;
26739 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26740 width = this.thumbEl.getWidth();
26741 this.baseScale = width / this.imageEl.OriginWidth;
26747 if(this.baseRotate == 6 || this.baseRotate == 8){
26749 width = this.thumbEl.getHeight();
26750 this.baseScale = width / this.imageEl.OriginHeight;
26752 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26753 height = this.thumbEl.getWidth();
26754 this.baseScale = height / this.imageEl.OriginHeight;
26757 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26758 height = this.thumbEl.getWidth();
26759 this.baseScale = height / this.imageEl.OriginHeight;
26761 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26762 width = this.thumbEl.getHeight();
26763 this.baseScale = width / this.imageEl.OriginWidth;
26770 width = this.thumbEl.getWidth();
26771 this.baseScale = width / this.imageEl.OriginWidth;
26773 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26774 height = this.thumbEl.getHeight();
26775 this.baseScale = height / this.imageEl.OriginHeight;
26778 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26780 height = this.thumbEl.getHeight();
26781 this.baseScale = height / this.imageEl.OriginHeight;
26783 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26784 width = this.thumbEl.getWidth();
26785 this.baseScale = width / this.imageEl.OriginWidth;
26793 getScaleLevel : function()
26795 return this.baseScale * Math.pow(1.1, this.scale);
26798 onTouchStart : function(e)
26800 if(!this.canvasLoaded){
26801 this.beforeSelectFile(e);
26805 var touches = e.browserEvent.touches;
26811 if(touches.length == 1){
26812 this.onMouseDown(e);
26816 if(touches.length != 2){
26822 for(var i = 0, finger; finger = touches[i]; i++){
26823 coords.push(finger.pageX, finger.pageY);
26826 var x = Math.pow(coords[0] - coords[2], 2);
26827 var y = Math.pow(coords[1] - coords[3], 2);
26829 this.startDistance = Math.sqrt(x + y);
26831 this.startScale = this.scale;
26833 this.pinching = true;
26834 this.dragable = false;
26838 onTouchMove : function(e)
26840 if(!this.pinching && !this.dragable){
26844 var touches = e.browserEvent.touches;
26851 this.onMouseMove(e);
26857 for(var i = 0, finger; finger = touches[i]; i++){
26858 coords.push(finger.pageX, finger.pageY);
26861 var x = Math.pow(coords[0] - coords[2], 2);
26862 var y = Math.pow(coords[1] - coords[3], 2);
26864 this.endDistance = Math.sqrt(x + y);
26866 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26868 if(!this.zoomable()){
26869 this.scale = this.startScale;
26877 onTouchEnd : function(e)
26879 this.pinching = false;
26880 this.dragable = false;
26884 process : function(file, crop)
26887 this.maskEl.mask(this.loadingText);
26890 this.xhr = new XMLHttpRequest();
26892 file.xhr = this.xhr;
26894 this.xhr.open(this.method, this.url, true);
26897 "Accept": "application/json",
26898 "Cache-Control": "no-cache",
26899 "X-Requested-With": "XMLHttpRequest"
26902 for (var headerName in headers) {
26903 var headerValue = headers[headerName];
26905 this.xhr.setRequestHeader(headerName, headerValue);
26911 this.xhr.onload = function()
26913 _this.xhrOnLoad(_this.xhr);
26916 this.xhr.onerror = function()
26918 _this.xhrOnError(_this.xhr);
26921 var formData = new FormData();
26923 formData.append('returnHTML', 'NO');
26926 formData.append('crop', crop);
26929 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26930 formData.append(this.paramName, file, file.name);
26933 if(typeof(file.filename) != 'undefined'){
26934 formData.append('filename', file.filename);
26937 if(typeof(file.mimetype) != 'undefined'){
26938 formData.append('mimetype', file.mimetype);
26941 if(this.fireEvent('arrange', this, formData) != false){
26942 this.xhr.send(formData);
26946 xhrOnLoad : function(xhr)
26949 this.maskEl.unmask();
26952 if (xhr.readyState !== 4) {
26953 this.fireEvent('exception', this, xhr);
26957 var response = Roo.decode(xhr.responseText);
26959 if(!response.success){
26960 this.fireEvent('exception', this, xhr);
26964 var response = Roo.decode(xhr.responseText);
26966 this.fireEvent('upload', this, response);
26970 xhrOnError : function()
26973 this.maskEl.unmask();
26976 Roo.log('xhr on error');
26978 var response = Roo.decode(xhr.responseText);
26984 prepare : function(file)
26987 this.maskEl.mask(this.loadingText);
26993 if(typeof(file) === 'string'){
26994 this.loadCanvas(file);
26998 if(!file || !this.urlAPI){
27003 this.cropType = file.type;
27007 if(this.fireEvent('prepare', this, this.file) != false){
27009 var reader = new FileReader();
27011 reader.onload = function (e) {
27012 if (e.target.error) {
27013 Roo.log(e.target.error);
27017 var buffer = e.target.result,
27018 dataView = new DataView(buffer),
27020 maxOffset = dataView.byteLength - 4,
27024 if (dataView.getUint16(0) === 0xffd8) {
27025 while (offset < maxOffset) {
27026 markerBytes = dataView.getUint16(offset);
27028 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27029 markerLength = dataView.getUint16(offset + 2) + 2;
27030 if (offset + markerLength > dataView.byteLength) {
27031 Roo.log('Invalid meta data: Invalid segment size.');
27035 if(markerBytes == 0xffe1){
27036 _this.parseExifData(
27043 offset += markerLength;
27053 var url = _this.urlAPI.createObjectURL(_this.file);
27055 _this.loadCanvas(url);
27060 reader.readAsArrayBuffer(this.file);
27066 parseExifData : function(dataView, offset, length)
27068 var tiffOffset = offset + 10,
27072 if (dataView.getUint32(offset + 4) !== 0x45786966) {
27073 // No Exif data, might be XMP data instead
27077 // Check for the ASCII code for "Exif" (0x45786966):
27078 if (dataView.getUint32(offset + 4) !== 0x45786966) {
27079 // No Exif data, might be XMP data instead
27082 if (tiffOffset + 8 > dataView.byteLength) {
27083 Roo.log('Invalid Exif data: Invalid segment size.');
27086 // Check for the two null bytes:
27087 if (dataView.getUint16(offset + 8) !== 0x0000) {
27088 Roo.log('Invalid Exif data: Missing byte alignment offset.');
27091 // Check the byte alignment:
27092 switch (dataView.getUint16(tiffOffset)) {
27094 littleEndian = true;
27097 littleEndian = false;
27100 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27103 // Check for the TIFF tag marker (0x002A):
27104 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27105 Roo.log('Invalid Exif data: Missing TIFF marker.');
27108 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27109 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27111 this.parseExifTags(
27114 tiffOffset + dirOffset,
27119 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27124 if (dirOffset + 6 > dataView.byteLength) {
27125 Roo.log('Invalid Exif data: Invalid directory offset.');
27128 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27129 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27130 if (dirEndOffset + 4 > dataView.byteLength) {
27131 Roo.log('Invalid Exif data: Invalid directory size.');
27134 for (i = 0; i < tagsNumber; i += 1) {
27138 dirOffset + 2 + 12 * i, // tag offset
27142 // Return the offset to the next directory:
27143 return dataView.getUint32(dirEndOffset, littleEndian);
27146 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
27148 var tag = dataView.getUint16(offset, littleEndian);
27150 this.exif[tag] = this.getExifValue(
27154 dataView.getUint16(offset + 2, littleEndian), // tag type
27155 dataView.getUint32(offset + 4, littleEndian), // tag length
27160 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27162 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27171 Roo.log('Invalid Exif data: Invalid tag type.');
27175 tagSize = tagType.size * length;
27176 // Determine if the value is contained in the dataOffset bytes,
27177 // or if the value at the dataOffset is a pointer to the actual data:
27178 dataOffset = tagSize > 4 ?
27179 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27180 if (dataOffset + tagSize > dataView.byteLength) {
27181 Roo.log('Invalid Exif data: Invalid data offset.');
27184 if (length === 1) {
27185 return tagType.getValue(dataView, dataOffset, littleEndian);
27188 for (i = 0; i < length; i += 1) {
27189 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27192 if (tagType.ascii) {
27194 // Concatenate the chars:
27195 for (i = 0; i < values.length; i += 1) {
27197 // Ignore the terminating NULL byte(s):
27198 if (c === '\u0000') {
27210 Roo.apply(Roo.bootstrap.UploadCropbox, {
27212 'Orientation': 0x0112
27216 1: 0, //'top-left',
27218 3: 180, //'bottom-right',
27219 // 4: 'bottom-left',
27221 6: 90, //'right-top',
27222 // 7: 'right-bottom',
27223 8: 270 //'left-bottom'
27227 // byte, 8-bit unsigned int:
27229 getValue: function (dataView, dataOffset) {
27230 return dataView.getUint8(dataOffset);
27234 // ascii, 8-bit byte:
27236 getValue: function (dataView, dataOffset) {
27237 return String.fromCharCode(dataView.getUint8(dataOffset));
27242 // short, 16 bit int:
27244 getValue: function (dataView, dataOffset, littleEndian) {
27245 return dataView.getUint16(dataOffset, littleEndian);
27249 // long, 32 bit int:
27251 getValue: function (dataView, dataOffset, littleEndian) {
27252 return dataView.getUint32(dataOffset, littleEndian);
27256 // rational = two long values, first is numerator, second is denominator:
27258 getValue: function (dataView, dataOffset, littleEndian) {
27259 return dataView.getUint32(dataOffset, littleEndian) /
27260 dataView.getUint32(dataOffset + 4, littleEndian);
27264 // slong, 32 bit signed int:
27266 getValue: function (dataView, dataOffset, littleEndian) {
27267 return dataView.getInt32(dataOffset, littleEndian);
27271 // srational, two slongs, first is numerator, second is denominator:
27273 getValue: function (dataView, dataOffset, littleEndian) {
27274 return dataView.getInt32(dataOffset, littleEndian) /
27275 dataView.getInt32(dataOffset + 4, littleEndian);
27285 cls : 'btn-group roo-upload-cropbox-rotate-left',
27286 action : 'rotate-left',
27290 cls : 'btn btn-default',
27291 html : '<i class="fa fa-undo"></i>'
27297 cls : 'btn-group roo-upload-cropbox-picture',
27298 action : 'picture',
27302 cls : 'btn btn-default',
27303 html : '<i class="fa fa-picture-o"></i>'
27309 cls : 'btn-group roo-upload-cropbox-rotate-right',
27310 action : 'rotate-right',
27314 cls : 'btn btn-default',
27315 html : '<i class="fa fa-repeat"></i>'
27323 cls : 'btn-group roo-upload-cropbox-rotate-left',
27324 action : 'rotate-left',
27328 cls : 'btn btn-default',
27329 html : '<i class="fa fa-undo"></i>'
27335 cls : 'btn-group roo-upload-cropbox-download',
27336 action : 'download',
27340 cls : 'btn btn-default',
27341 html : '<i class="fa fa-download"></i>'
27347 cls : 'btn-group roo-upload-cropbox-crop',
27352 cls : 'btn btn-default',
27353 html : '<i class="fa fa-crop"></i>'
27359 cls : 'btn-group roo-upload-cropbox-trash',
27364 cls : 'btn btn-default',
27365 html : '<i class="fa fa-trash"></i>'
27371 cls : 'btn-group roo-upload-cropbox-rotate-right',
27372 action : 'rotate-right',
27376 cls : 'btn btn-default',
27377 html : '<i class="fa fa-repeat"></i>'
27385 cls : 'btn-group roo-upload-cropbox-rotate-left',
27386 action : 'rotate-left',
27390 cls : 'btn btn-default',
27391 html : '<i class="fa fa-undo"></i>'
27397 cls : 'btn-group roo-upload-cropbox-rotate-right',
27398 action : 'rotate-right',
27402 cls : 'btn btn-default',
27403 html : '<i class="fa fa-repeat"></i>'
27416 * @class Roo.bootstrap.DocumentManager
27417 * @extends Roo.bootstrap.Component
27418 * Bootstrap DocumentManager class
27419 * @cfg {String} paramName default 'imageUpload'
27420 * @cfg {String} toolTipName default 'filename'
27421 * @cfg {String} method default POST
27422 * @cfg {String} url action url
27423 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27424 * @cfg {Boolean} multiple multiple upload default true
27425 * @cfg {Number} thumbSize default 300
27426 * @cfg {String} fieldLabel
27427 * @cfg {Number} labelWidth default 4
27428 * @cfg {String} labelAlign (left|top) default left
27429 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27432 * Create a new DocumentManager
27433 * @param {Object} config The config object
27436 Roo.bootstrap.DocumentManager = function(config){
27437 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27440 this.delegates = [];
27445 * Fire when initial the DocumentManager
27446 * @param {Roo.bootstrap.DocumentManager} this
27451 * inspect selected file
27452 * @param {Roo.bootstrap.DocumentManager} this
27453 * @param {File} file
27458 * Fire when xhr load exception
27459 * @param {Roo.bootstrap.DocumentManager} this
27460 * @param {XMLHttpRequest} xhr
27462 "exception" : true,
27464 * @event afterupload
27465 * Fire when xhr load exception
27466 * @param {Roo.bootstrap.DocumentManager} this
27467 * @param {XMLHttpRequest} xhr
27469 "afterupload" : true,
27472 * prepare the form data
27473 * @param {Roo.bootstrap.DocumentManager} this
27474 * @param {Object} formData
27479 * Fire when remove the file
27480 * @param {Roo.bootstrap.DocumentManager} this
27481 * @param {Object} file
27486 * Fire after refresh the file
27487 * @param {Roo.bootstrap.DocumentManager} this
27492 * Fire after click the image
27493 * @param {Roo.bootstrap.DocumentManager} this
27494 * @param {Object} file
27499 * Fire when upload a image and editable set to true
27500 * @param {Roo.bootstrap.DocumentManager} this
27501 * @param {Object} file
27505 * @event beforeselectfile
27506 * Fire before select file
27507 * @param {Roo.bootstrap.DocumentManager} this
27509 "beforeselectfile" : true,
27512 * Fire before process file
27513 * @param {Roo.bootstrap.DocumentManager} this
27514 * @param {Object} file
27521 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
27530 paramName : 'imageUpload',
27531 toolTipName : 'filename',
27534 labelAlign : 'left',
27539 getAutoCreate : function()
27541 var managerWidget = {
27543 cls : 'roo-document-manager',
27547 cls : 'roo-document-manager-selector',
27552 cls : 'roo-document-manager-uploader',
27556 cls : 'roo-document-manager-upload-btn',
27557 html : '<i class="fa fa-plus"></i>'
27568 cls : 'column col-md-12',
27573 if(this.fieldLabel.length){
27578 cls : 'column col-md-12',
27579 html : this.fieldLabel
27583 cls : 'column col-md-12',
27588 if(this.labelAlign == 'left'){
27592 cls : 'column col-md-' + this.labelWidth,
27593 html : this.fieldLabel
27597 cls : 'column col-md-' + (12 - this.labelWidth),
27607 cls : 'row clearfix',
27615 initEvents : function()
27617 this.managerEl = this.el.select('.roo-document-manager', true).first();
27618 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27620 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27621 this.selectorEl.hide();
27624 this.selectorEl.attr('multiple', 'multiple');
27627 this.selectorEl.on('change', this.onFileSelected, this);
27629 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27630 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27632 this.uploader.on('click', this.onUploaderClick, this);
27634 this.renderProgressDialog();
27638 window.addEventListener("resize", function() { _this.refresh(); } );
27640 this.fireEvent('initial', this);
27643 renderProgressDialog : function()
27647 this.progressDialog = new Roo.bootstrap.Modal({
27648 cls : 'roo-document-manager-progress-dialog',
27649 allow_close : false,
27659 btnclick : function() {
27660 _this.uploadCancel();
27666 this.progressDialog.render(Roo.get(document.body));
27668 this.progress = new Roo.bootstrap.Progress({
27669 cls : 'roo-document-manager-progress',
27674 this.progress.render(this.progressDialog.getChildContainer());
27676 this.progressBar = new Roo.bootstrap.ProgressBar({
27677 cls : 'roo-document-manager-progress-bar',
27680 aria_valuemax : 12,
27684 this.progressBar.render(this.progress.getChildContainer());
27687 onUploaderClick : function(e)
27689 e.preventDefault();
27691 if(this.fireEvent('beforeselectfile', this) != false){
27692 this.selectorEl.dom.click();
27697 onFileSelected : function(e)
27699 e.preventDefault();
27701 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27705 Roo.each(this.selectorEl.dom.files, function(file){
27706 if(this.fireEvent('inspect', this, file) != false){
27707 this.files.push(file);
27717 this.selectorEl.dom.value = '';
27719 if(!this.files.length){
27723 if(this.boxes > 0 && this.files.length > this.boxes){
27724 this.files = this.files.slice(0, this.boxes);
27727 this.uploader.show();
27729 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27730 this.uploader.hide();
27739 Roo.each(this.files, function(file){
27741 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27742 var f = this.renderPreview(file);
27747 if(file.type.indexOf('image') != -1){
27748 this.delegates.push(
27750 _this.process(file);
27751 }).createDelegate(this)
27759 _this.process(file);
27760 }).createDelegate(this)
27765 this.files = files;
27767 this.delegates = this.delegates.concat(docs);
27769 if(!this.delegates.length){
27774 this.progressBar.aria_valuemax = this.delegates.length;
27781 arrange : function()
27783 if(!this.delegates.length){
27784 this.progressDialog.hide();
27789 var delegate = this.delegates.shift();
27791 this.progressDialog.show();
27793 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27795 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27800 refresh : function()
27802 this.uploader.show();
27804 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27805 this.uploader.hide();
27808 Roo.isTouch ? this.closable(false) : this.closable(true);
27810 this.fireEvent('refresh', this);
27813 onRemove : function(e, el, o)
27815 e.preventDefault();
27817 this.fireEvent('remove', this, o);
27821 remove : function(o)
27825 Roo.each(this.files, function(file){
27826 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27835 this.files = files;
27842 Roo.each(this.files, function(file){
27847 file.target.remove();
27856 onClick : function(e, el, o)
27858 e.preventDefault();
27860 this.fireEvent('click', this, o);
27864 closable : function(closable)
27866 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27868 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27880 xhrOnLoad : function(xhr)
27882 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27886 if (xhr.readyState !== 4) {
27888 this.fireEvent('exception', this, xhr);
27892 var response = Roo.decode(xhr.responseText);
27894 if(!response.success){
27896 this.fireEvent('exception', this, xhr);
27900 var file = this.renderPreview(response.data);
27902 this.files.push(file);
27906 this.fireEvent('afterupload', this, xhr);
27910 xhrOnError : function(xhr)
27912 Roo.log('xhr on error');
27914 var response = Roo.decode(xhr.responseText);
27921 process : function(file)
27923 if(this.fireEvent('process', this, file) !== false){
27924 if(this.editable && file.type.indexOf('image') != -1){
27925 this.fireEvent('edit', this, file);
27929 this.uploadStart(file, false);
27936 uploadStart : function(file, crop)
27938 this.xhr = new XMLHttpRequest();
27940 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27945 file.xhr = this.xhr;
27947 this.managerEl.createChild({
27949 cls : 'roo-document-manager-loading',
27953 tooltip : file.name,
27954 cls : 'roo-document-manager-thumb',
27955 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27961 this.xhr.open(this.method, this.url, true);
27964 "Accept": "application/json",
27965 "Cache-Control": "no-cache",
27966 "X-Requested-With": "XMLHttpRequest"
27969 for (var headerName in headers) {
27970 var headerValue = headers[headerName];
27972 this.xhr.setRequestHeader(headerName, headerValue);
27978 this.xhr.onload = function()
27980 _this.xhrOnLoad(_this.xhr);
27983 this.xhr.onerror = function()
27985 _this.xhrOnError(_this.xhr);
27988 var formData = new FormData();
27990 formData.append('returnHTML', 'NO');
27993 formData.append('crop', crop);
27996 formData.append(this.paramName, file, file.name);
28003 if(this.fireEvent('prepare', this, formData, options) != false){
28005 if(options.manually){
28009 this.xhr.send(formData);
28013 this.uploadCancel();
28016 uploadCancel : function()
28022 this.delegates = [];
28024 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28031 renderPreview : function(file)
28033 if(typeof(file.target) != 'undefined' && file.target){
28037 var previewEl = this.managerEl.createChild({
28039 cls : 'roo-document-manager-preview',
28043 tooltip : file[this.toolTipName],
28044 cls : 'roo-document-manager-thumb',
28045 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
28050 html : '<i class="fa fa-times-circle"></i>'
28055 var close = previewEl.select('button.close', true).first();
28057 close.on('click', this.onRemove, this, file);
28059 file.target = previewEl;
28061 var image = previewEl.select('img', true).first();
28065 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28067 image.on('click', this.onClick, this, file);
28073 onPreviewLoad : function(file, image)
28075 if(typeof(file.target) == 'undefined' || !file.target){
28079 var width = image.dom.naturalWidth || image.dom.width;
28080 var height = image.dom.naturalHeight || image.dom.height;
28082 if(width > height){
28083 file.target.addClass('wide');
28087 file.target.addClass('tall');
28092 uploadFromSource : function(file, crop)
28094 this.xhr = new XMLHttpRequest();
28096 this.managerEl.createChild({
28098 cls : 'roo-document-manager-loading',
28102 tooltip : file.name,
28103 cls : 'roo-document-manager-thumb',
28104 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28110 this.xhr.open(this.method, this.url, true);
28113 "Accept": "application/json",
28114 "Cache-Control": "no-cache",
28115 "X-Requested-With": "XMLHttpRequest"
28118 for (var headerName in headers) {
28119 var headerValue = headers[headerName];
28121 this.xhr.setRequestHeader(headerName, headerValue);
28127 this.xhr.onload = function()
28129 _this.xhrOnLoad(_this.xhr);
28132 this.xhr.onerror = function()
28134 _this.xhrOnError(_this.xhr);
28137 var formData = new FormData();
28139 formData.append('returnHTML', 'NO');
28141 formData.append('crop', crop);
28143 if(typeof(file.filename) != 'undefined'){
28144 formData.append('filename', file.filename);
28147 if(typeof(file.mimetype) != 'undefined'){
28148 formData.append('mimetype', file.mimetype);
28151 if(this.fireEvent('prepare', this, formData) != false){
28152 this.xhr.send(formData);
28162 * @class Roo.bootstrap.DocumentViewer
28163 * @extends Roo.bootstrap.Component
28164 * Bootstrap DocumentViewer class
28165 * @cfg {Boolean} showDownload (true|false) show download button (default true)
28166 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28169 * Create a new DocumentViewer
28170 * @param {Object} config The config object
28173 Roo.bootstrap.DocumentViewer = function(config){
28174 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28179 * Fire after initEvent
28180 * @param {Roo.bootstrap.DocumentViewer} this
28186 * @param {Roo.bootstrap.DocumentViewer} this
28191 * Fire after download button
28192 * @param {Roo.bootstrap.DocumentViewer} this
28197 * Fire after trash button
28198 * @param {Roo.bootstrap.DocumentViewer} this
28205 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
28207 showDownload : true,
28211 getAutoCreate : function()
28215 cls : 'roo-document-viewer',
28219 cls : 'roo-document-viewer-body',
28223 cls : 'roo-document-viewer-thumb',
28227 cls : 'roo-document-viewer-image'
28235 cls : 'roo-document-viewer-footer',
28238 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
28242 cls : 'btn-group roo-document-viewer-download',
28246 cls : 'btn btn-default',
28247 html : '<i class="fa fa-download"></i>'
28253 cls : 'btn-group roo-document-viewer-trash',
28257 cls : 'btn btn-default',
28258 html : '<i class="fa fa-trash"></i>'
28271 initEvents : function()
28273 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
28274 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
28276 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
28277 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
28279 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
28280 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
28282 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
28283 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
28285 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
28286 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
28288 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
28289 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
28291 this.bodyEl.on('click', this.onClick, this);
28292 this.downloadBtn.on('click', this.onDownload, this);
28293 this.trashBtn.on('click', this.onTrash, this);
28295 this.downloadBtn.hide();
28296 this.trashBtn.hide();
28298 if(this.showDownload){
28299 this.downloadBtn.show();
28302 if(this.showTrash){
28303 this.trashBtn.show();
28306 if(!this.showDownload && !this.showTrash) {
28307 this.footerEl.hide();
28312 initial : function()
28314 this.fireEvent('initial', this);
28318 onClick : function(e)
28320 e.preventDefault();
28322 this.fireEvent('click', this);
28325 onDownload : function(e)
28327 e.preventDefault();
28329 this.fireEvent('download', this);
28332 onTrash : function(e)
28334 e.preventDefault();
28336 this.fireEvent('trash', this);
28348 * @class Roo.bootstrap.NavProgressBar
28349 * @extends Roo.bootstrap.Component
28350 * Bootstrap NavProgressBar class
28353 * Create a new nav progress bar
28354 * @param {Object} config The config object
28357 Roo.bootstrap.NavProgressBar = function(config){
28358 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
28360 this.bullets = this.bullets || [];
28362 // Roo.bootstrap.NavProgressBar.register(this);
28366 * Fires when the active item changes
28367 * @param {Roo.bootstrap.NavProgressBar} this
28368 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28369 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
28376 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
28381 getAutoCreate : function()
28383 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28387 cls : 'roo-navigation-bar-group',
28391 cls : 'roo-navigation-top-bar'
28395 cls : 'roo-navigation-bullets-bar',
28399 cls : 'roo-navigation-bar'
28406 cls : 'roo-navigation-bottom-bar'
28416 initEvents: function()
28421 onRender : function(ct, position)
28423 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28425 if(this.bullets.length){
28426 Roo.each(this.bullets, function(b){
28435 addItem : function(cfg)
28437 var item = new Roo.bootstrap.NavProgressItem(cfg);
28439 item.parentId = this.id;
28440 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28443 var top = new Roo.bootstrap.Element({
28445 cls : 'roo-navigation-bar-text'
28448 var bottom = new Roo.bootstrap.Element({
28450 cls : 'roo-navigation-bar-text'
28453 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28454 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28456 var topText = new Roo.bootstrap.Element({
28458 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28461 var bottomText = new Roo.bootstrap.Element({
28463 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28466 topText.onRender(top.el, null);
28467 bottomText.onRender(bottom.el, null);
28470 item.bottomEl = bottom;
28473 this.barItems.push(item);
28478 getActive : function()
28480 var active = false;
28482 Roo.each(this.barItems, function(v){
28484 if (!v.isActive()) {
28496 setActiveItem : function(item)
28500 Roo.each(this.barItems, function(v){
28501 if (v.rid == item.rid) {
28505 if (v.isActive()) {
28506 v.setActive(false);
28511 item.setActive(true);
28513 this.fireEvent('changed', this, item, prev);
28516 getBarItem: function(rid)
28520 Roo.each(this.barItems, function(e) {
28521 if (e.rid != rid) {
28532 indexOfItem : function(item)
28536 Roo.each(this.barItems, function(v, i){
28538 if (v.rid != item.rid) {
28549 setActiveNext : function()
28551 var i = this.indexOfItem(this.getActive());
28553 if (i > this.barItems.length) {
28557 this.setActiveItem(this.barItems[i+1]);
28560 setActivePrev : function()
28562 var i = this.indexOfItem(this.getActive());
28568 this.setActiveItem(this.barItems[i-1]);
28571 format : function()
28573 if(!this.barItems.length){
28577 var width = 100 / this.barItems.length;
28579 Roo.each(this.barItems, function(i){
28580 i.el.setStyle('width', width + '%');
28581 i.topEl.el.setStyle('width', width + '%');
28582 i.bottomEl.el.setStyle('width', width + '%');
28591 * Nav Progress Item
28596 * @class Roo.bootstrap.NavProgressItem
28597 * @extends Roo.bootstrap.Component
28598 * Bootstrap NavProgressItem class
28599 * @cfg {String} rid the reference id
28600 * @cfg {Boolean} active (true|false) Is item active default false
28601 * @cfg {Boolean} disabled (true|false) Is item active default false
28602 * @cfg {String} html
28603 * @cfg {String} position (top|bottom) text position default bottom
28604 * @cfg {String} icon show icon instead of number
28607 * Create a new NavProgressItem
28608 * @param {Object} config The config object
28610 Roo.bootstrap.NavProgressItem = function(config){
28611 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28616 * The raw click event for the entire grid.
28617 * @param {Roo.bootstrap.NavProgressItem} this
28618 * @param {Roo.EventObject} e
28625 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
28631 position : 'bottom',
28634 getAutoCreate : function()
28636 var iconCls = 'roo-navigation-bar-item-icon';
28638 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28642 cls: 'roo-navigation-bar-item',
28652 cfg.cls += ' active';
28655 cfg.cls += ' disabled';
28661 disable : function()
28663 this.setDisabled(true);
28666 enable : function()
28668 this.setDisabled(false);
28671 initEvents: function()
28673 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28675 this.iconEl.on('click', this.onClick, this);
28678 onClick : function(e)
28680 e.preventDefault();
28686 if(this.fireEvent('click', this, e) === false){
28690 this.parent().setActiveItem(this);
28693 isActive: function ()
28695 return this.active;
28698 setActive : function(state)
28700 if(this.active == state){
28704 this.active = state;
28707 this.el.addClass('active');
28711 this.el.removeClass('active');
28716 setDisabled : function(state)
28718 if(this.disabled == state){
28722 this.disabled = state;
28725 this.el.addClass('disabled');
28729 this.el.removeClass('disabled');
28732 tooltipEl : function()
28734 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28747 * @class Roo.bootstrap.FieldLabel
28748 * @extends Roo.bootstrap.Component
28749 * Bootstrap FieldLabel class
28750 * @cfg {String} html contents of the element
28751 * @cfg {String} tag tag of the element default label
28752 * @cfg {String} cls class of the element
28753 * @cfg {String} target label target
28754 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28755 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28756 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28757 * @cfg {String} iconTooltip default "This field is required"
28760 * Create a new FieldLabel
28761 * @param {Object} config The config object
28764 Roo.bootstrap.FieldLabel = function(config){
28765 Roo.bootstrap.Element.superclass.constructor.call(this, config);
28770 * Fires after the field has been marked as invalid.
28771 * @param {Roo.form.FieldLabel} this
28772 * @param {String} msg The validation message
28777 * Fires after the field has been validated with no errors.
28778 * @param {Roo.form.FieldLabel} this
28784 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
28791 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28792 validClass : 'text-success fa fa-lg fa-check',
28793 iconTooltip : 'This field is required',
28795 getAutoCreate : function(){
28799 cls : 'roo-bootstrap-field-label ' + this.cls,
28805 tooltip : this.iconTooltip
28817 initEvents: function()
28819 Roo.bootstrap.Element.superclass.initEvents.call(this);
28821 this.iconEl = this.el.select('i', true).first();
28823 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28825 Roo.bootstrap.FieldLabel.register(this);
28829 * Mark this field as valid
28831 markValid : function()
28833 this.iconEl.show();
28835 this.iconEl.removeClass(this.invalidClass);
28837 this.iconEl.addClass(this.validClass);
28839 this.fireEvent('valid', this);
28843 * Mark this field as invalid
28844 * @param {String} msg The validation message
28846 markInvalid : function(msg)
28848 this.iconEl.show();
28850 this.iconEl.removeClass(this.validClass);
28852 this.iconEl.addClass(this.invalidClass);
28854 this.fireEvent('invalid', this, msg);
28860 Roo.apply(Roo.bootstrap.FieldLabel, {
28865 * register a FieldLabel Group
28866 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28868 register : function(label)
28870 if(this.groups.hasOwnProperty(label.target)){
28874 this.groups[label.target] = label;
28878 * fetch a FieldLabel Group based on the target
28879 * @param {string} target
28880 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28882 get: function(target) {
28883 if (typeof(this.groups[target]) == 'undefined') {
28887 return this.groups[target] ;
28896 * page DateSplitField.
28902 * @class Roo.bootstrap.DateSplitField
28903 * @extends Roo.bootstrap.Component
28904 * Bootstrap DateSplitField class
28905 * @cfg {string} fieldLabel - the label associated
28906 * @cfg {Number} labelWidth set the width of label (0-12)
28907 * @cfg {String} labelAlign (top|left)
28908 * @cfg {Boolean} dayAllowBlank (true|false) default false
28909 * @cfg {Boolean} monthAllowBlank (true|false) default false
28910 * @cfg {Boolean} yearAllowBlank (true|false) default false
28911 * @cfg {string} dayPlaceholder
28912 * @cfg {string} monthPlaceholder
28913 * @cfg {string} yearPlaceholder
28914 * @cfg {string} dayFormat default 'd'
28915 * @cfg {string} monthFormat default 'm'
28916 * @cfg {string} yearFormat default 'Y'
28920 * Create a new DateSplitField
28921 * @param {Object} config The config object
28924 Roo.bootstrap.DateSplitField = function(config){
28925 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28931 * getting the data of years
28932 * @param {Roo.bootstrap.DateSplitField} this
28933 * @param {Object} years
28938 * getting the data of days
28939 * @param {Roo.bootstrap.DateSplitField} this
28940 * @param {Object} days
28945 * Fires after the field has been marked as invalid.
28946 * @param {Roo.form.Field} this
28947 * @param {String} msg The validation message
28952 * Fires after the field has been validated with no errors.
28953 * @param {Roo.form.Field} this
28959 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
28962 labelAlign : 'top',
28964 dayAllowBlank : false,
28965 monthAllowBlank : false,
28966 yearAllowBlank : false,
28967 dayPlaceholder : '',
28968 monthPlaceholder : '',
28969 yearPlaceholder : '',
28973 isFormField : true,
28975 getAutoCreate : function()
28979 cls : 'row roo-date-split-field-group',
28984 cls : 'form-hidden-field roo-date-split-field-group-value',
28990 if(this.fieldLabel){
28993 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28997 html : this.fieldLabel
29003 Roo.each(['day', 'month', 'year'], function(t){
29006 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
29013 inputEl: function ()
29015 return this.el.select('.roo-date-split-field-group-value', true).first();
29018 onRender : function(ct, position)
29022 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29024 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29026 this.dayField = new Roo.bootstrap.ComboBox({
29027 allowBlank : this.dayAllowBlank,
29028 alwaysQuery : true,
29029 displayField : 'value',
29032 forceSelection : true,
29034 placeholder : this.dayPlaceholder,
29035 selectOnFocus : true,
29036 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29037 triggerAction : 'all',
29039 valueField : 'value',
29040 store : new Roo.data.SimpleStore({
29041 data : (function() {
29043 _this.fireEvent('days', _this, days);
29046 fields : [ 'value' ]
29049 select : function (_self, record, index)
29051 _this.setValue(_this.getValue());
29056 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29058 this.monthField = new Roo.bootstrap.MonthField({
29059 after : '<i class=\"fa fa-calendar\"></i>',
29060 allowBlank : this.monthAllowBlank,
29061 placeholder : this.monthPlaceholder,
29064 render : function (_self)
29066 this.el.select('span.input-group-addon', true).first().on('click', function(e){
29067 e.preventDefault();
29071 select : function (_self, oldvalue, newvalue)
29073 _this.setValue(_this.getValue());
29078 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29080 this.yearField = new Roo.bootstrap.ComboBox({
29081 allowBlank : this.yearAllowBlank,
29082 alwaysQuery : true,
29083 displayField : 'value',
29086 forceSelection : true,
29088 placeholder : this.yearPlaceholder,
29089 selectOnFocus : true,
29090 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29091 triggerAction : 'all',
29093 valueField : 'value',
29094 store : new Roo.data.SimpleStore({
29095 data : (function() {
29097 _this.fireEvent('years', _this, years);
29100 fields : [ 'value' ]
29103 select : function (_self, record, index)
29105 _this.setValue(_this.getValue());
29110 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29113 setValue : function(v, format)
29115 this.inputEl.dom.value = v;
29117 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29119 var d = Date.parseDate(v, f);
29126 this.setDay(d.format(this.dayFormat));
29127 this.setMonth(d.format(this.monthFormat));
29128 this.setYear(d.format(this.yearFormat));
29135 setDay : function(v)
29137 this.dayField.setValue(v);
29138 this.inputEl.dom.value = this.getValue();
29143 setMonth : function(v)
29145 this.monthField.setValue(v, true);
29146 this.inputEl.dom.value = this.getValue();
29151 setYear : function(v)
29153 this.yearField.setValue(v);
29154 this.inputEl.dom.value = this.getValue();
29159 getDay : function()
29161 return this.dayField.getValue();
29164 getMonth : function()
29166 return this.monthField.getValue();
29169 getYear : function()
29171 return this.yearField.getValue();
29174 getValue : function()
29176 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
29178 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
29188 this.inputEl.dom.value = '';
29193 validate : function()
29195 var d = this.dayField.validate();
29196 var m = this.monthField.validate();
29197 var y = this.yearField.validate();
29202 (!this.dayAllowBlank && !d) ||
29203 (!this.monthAllowBlank && !m) ||
29204 (!this.yearAllowBlank && !y)
29209 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
29218 this.markInvalid();
29223 markValid : function()
29226 var label = this.el.select('label', true).first();
29227 var icon = this.el.select('i.fa-star', true).first();
29233 this.fireEvent('valid', this);
29237 * Mark this field as invalid
29238 * @param {String} msg The validation message
29240 markInvalid : function(msg)
29243 var label = this.el.select('label', true).first();
29244 var icon = this.el.select('i.fa-star', true).first();
29246 if(label && !icon){
29247 this.el.select('.roo-date-split-field-label', true).createChild({
29249 cls : 'text-danger fa fa-lg fa-star',
29250 tooltip : 'This field is required',
29251 style : 'margin-right:5px;'
29255 this.fireEvent('invalid', this, msg);
29258 clearInvalid : function()
29260 var label = this.el.select('label', true).first();
29261 var icon = this.el.select('i.fa-star', true).first();
29267 this.fireEvent('valid', this);
29270 getName: function()
29280 * http://masonry.desandro.com
29282 * The idea is to render all the bricks based on vertical width...
29284 * The original code extends 'outlayer' - we might need to use that....
29290 * @class Roo.bootstrap.LayoutMasonry
29291 * @extends Roo.bootstrap.Component
29292 * Bootstrap Layout Masonry class
29295 * Create a new Element
29296 * @param {Object} config The config object
29299 Roo.bootstrap.LayoutMasonry = function(config){
29300 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
29306 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
29309 * @cfg {Boolean} isLayoutInstant = no animation?
29311 isLayoutInstant : false, // needed?
29314 * @cfg {Number} boxWidth width of the columns
29319 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
29324 * @cfg {Number} padWidth padding below box..
29329 * @cfg {Number} gutter gutter width..
29334 * @cfg {Number} maxCols maximum number of columns
29340 * @cfg {Boolean} isAutoInitial defalut true
29342 isAutoInitial : true,
29347 * @cfg {Boolean} isHorizontal defalut false
29349 isHorizontal : false,
29351 currentSize : null,
29357 bricks: null, //CompositeElement
29361 _isLayoutInited : false,
29363 // isAlternative : false, // only use for vertical layout...
29366 * @cfg {Number} alternativePadWidth padding below box..
29368 alternativePadWidth : 50,
29370 getAutoCreate : function(){
29374 cls: 'blog-masonary-wrapper ' + this.cls,
29376 cls : 'mas-boxes masonary'
29383 getChildContainer: function( )
29385 if (this.boxesEl) {
29386 return this.boxesEl;
29389 this.boxesEl = this.el.select('.mas-boxes').first();
29391 return this.boxesEl;
29395 initEvents : function()
29399 if(this.isAutoInitial){
29400 Roo.log('hook children rendered');
29401 this.on('childrenrendered', function() {
29402 Roo.log('children rendered');
29408 initial : function()
29410 this.currentSize = this.el.getBox(true);
29412 Roo.EventManager.onWindowResize(this.resize, this);
29414 if(!this.isAutoInitial){
29422 //this.layout.defer(500,this);
29426 resize : function()
29430 var cs = this.el.getBox(true);
29432 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29433 Roo.log("no change in with or X");
29437 this.currentSize = cs;
29443 layout : function()
29445 this._resetLayout();
29447 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29449 this.layoutItems( isInstant );
29451 this._isLayoutInited = true;
29455 _resetLayout : function()
29457 if(this.isHorizontal){
29458 this.horizontalMeasureColumns();
29462 this.verticalMeasureColumns();
29466 verticalMeasureColumns : function()
29468 this.getContainerWidth();
29470 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29471 // this.colWidth = Math.floor(this.containerWidth * 0.8);
29475 var boxWidth = this.boxWidth + this.padWidth;
29477 if(this.containerWidth < this.boxWidth){
29478 boxWidth = this.containerWidth
29481 var containerWidth = this.containerWidth;
29483 var cols = Math.floor(containerWidth / boxWidth);
29485 this.cols = Math.max( cols, 1 );
29487 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29489 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29491 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29493 this.colWidth = boxWidth + avail - this.padWidth;
29495 this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29496 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
29499 horizontalMeasureColumns : function()
29501 this.getContainerWidth();
29503 var boxWidth = this.boxWidth;
29505 if(this.containerWidth < boxWidth){
29506 boxWidth = this.containerWidth;
29509 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29511 this.el.setHeight(boxWidth);
29515 getContainerWidth : function()
29517 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
29520 layoutItems : function( isInstant )
29522 var items = Roo.apply([], this.bricks);
29524 if(this.isHorizontal){
29525 this._horizontalLayoutItems( items , isInstant );
29529 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29530 // this._verticalAlternativeLayoutItems( items , isInstant );
29534 this._verticalLayoutItems( items , isInstant );
29538 _verticalLayoutItems : function ( items , isInstant)
29540 if ( !items || !items.length ) {
29545 ['xs', 'xs', 'xs', 'tall'],
29546 ['xs', 'xs', 'tall'],
29547 ['xs', 'xs', 'sm'],
29548 ['xs', 'xs', 'xs'],
29554 ['sm', 'xs', 'xs'],
29558 ['tall', 'xs', 'xs', 'xs'],
29559 ['tall', 'xs', 'xs'],
29571 Roo.each(items, function(item, k){
29573 switch (item.size) {
29574 // these layouts take up a full box,
29585 boxes.push([item]);
29608 var filterPattern = function(box, length)
29616 var pattern = box.slice(0, length);
29620 Roo.each(pattern, function(i){
29621 format.push(i.size);
29624 Roo.each(standard, function(s){
29626 if(String(s) != String(format)){
29635 if(!match && length == 1){
29640 filterPattern(box, length - 1);
29644 queue.push(pattern);
29646 box = box.slice(length, box.length);
29648 filterPattern(box, 4);
29654 Roo.each(boxes, function(box, k){
29660 if(box.length == 1){
29665 filterPattern(box, 4);
29669 this._processVerticalLayoutQueue( queue, isInstant );
29673 // _verticalAlternativeLayoutItems : function( items , isInstant )
29675 // if ( !items || !items.length ) {
29679 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
29683 _horizontalLayoutItems : function ( items , isInstant)
29685 if ( !items || !items.length || items.length < 3) {
29691 var eItems = items.slice(0, 3);
29693 items = items.slice(3, items.length);
29696 ['xs', 'xs', 'xs', 'wide'],
29697 ['xs', 'xs', 'wide'],
29698 ['xs', 'xs', 'sm'],
29699 ['xs', 'xs', 'xs'],
29705 ['sm', 'xs', 'xs'],
29709 ['wide', 'xs', 'xs', 'xs'],
29710 ['wide', 'xs', 'xs'],
29723 Roo.each(items, function(item, k){
29725 switch (item.size) {
29736 boxes.push([item]);
29760 var filterPattern = function(box, length)
29768 var pattern = box.slice(0, length);
29772 Roo.each(pattern, function(i){
29773 format.push(i.size);
29776 Roo.each(standard, function(s){
29778 if(String(s) != String(format)){
29787 if(!match && length == 1){
29792 filterPattern(box, length - 1);
29796 queue.push(pattern);
29798 box = box.slice(length, box.length);
29800 filterPattern(box, 4);
29806 Roo.each(boxes, function(box, k){
29812 if(box.length == 1){
29817 filterPattern(box, 4);
29824 var pos = this.el.getBox(true);
29828 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29830 var hit_end = false;
29832 Roo.each(queue, function(box){
29836 Roo.each(box, function(b){
29838 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29848 Roo.each(box, function(b){
29850 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29853 mx = Math.max(mx, b.x);
29857 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29861 Roo.each(box, function(b){
29863 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29877 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29880 /** Sets position of item in DOM
29881 * @param {Element} item
29882 * @param {Number} x - horizontal position
29883 * @param {Number} y - vertical position
29884 * @param {Boolean} isInstant - disables transitions
29886 _processVerticalLayoutQueue : function( queue, isInstant )
29888 var pos = this.el.getBox(true);
29893 for (var i = 0; i < this.cols; i++){
29897 Roo.each(queue, function(box, k){
29899 var col = k % this.cols;
29901 Roo.each(box, function(b,kk){
29903 b.el.position('absolute');
29905 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29906 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29908 if(b.size == 'md-left' || b.size == 'md-right'){
29909 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29910 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29913 b.el.setWidth(width);
29914 b.el.setHeight(height);
29916 b.el.select('iframe',true).setSize(width,height);
29920 for (var i = 0; i < this.cols; i++){
29922 if(maxY[i] < maxY[col]){
29927 col = Math.min(col, i);
29931 x = pos.x + col * (this.colWidth + this.padWidth);
29935 var positions = [];
29937 switch (box.length){
29939 positions = this.getVerticalOneBoxColPositions(x, y, box);
29942 positions = this.getVerticalTwoBoxColPositions(x, y, box);
29945 positions = this.getVerticalThreeBoxColPositions(x, y, box);
29948 positions = this.getVerticalFourBoxColPositions(x, y, box);
29954 Roo.each(box, function(b,kk){
29956 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29958 var sz = b.el.getSize();
29960 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29968 for (var i = 0; i < this.cols; i++){
29969 mY = Math.max(mY, maxY[i]);
29972 this.el.setHeight(mY - pos.y);
29976 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29978 // var pos = this.el.getBox(true);
29981 // var maxX = pos.right;
29983 // var maxHeight = 0;
29985 // Roo.each(items, function(item, k){
29989 // item.el.position('absolute');
29991 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29993 // item.el.setWidth(width);
29995 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29997 // item.el.setHeight(height);
30000 // item.el.setXY([x, y], isInstant ? false : true);
30002 // item.el.setXY([maxX - width, y], isInstant ? false : true);
30005 // y = y + height + this.alternativePadWidth;
30007 // maxHeight = maxHeight + height + this.alternativePadWidth;
30011 // this.el.setHeight(maxHeight);
30015 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30017 var pos = this.el.getBox(true);
30022 var maxX = pos.right;
30024 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
30026 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30028 Roo.each(queue, function(box, k){
30030 Roo.each(box, function(b, kk){
30032 b.el.position('absolute');
30034 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30035 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30037 if(b.size == 'md-left' || b.size == 'md-right'){
30038 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30039 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30042 b.el.setWidth(width);
30043 b.el.setHeight(height);
30051 var positions = [];
30053 switch (box.length){
30055 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30058 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30061 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30064 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30070 Roo.each(box, function(b,kk){
30072 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30074 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30082 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30084 Roo.each(eItems, function(b,k){
30086 b.size = (k == 0) ? 'sm' : 'xs';
30087 b.x = (k == 0) ? 2 : 1;
30088 b.y = (k == 0) ? 2 : 1;
30090 b.el.position('absolute');
30092 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30094 b.el.setWidth(width);
30096 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30098 b.el.setHeight(height);
30102 var positions = [];
30105 x : maxX - this.unitWidth * 2 - this.gutter,
30110 x : maxX - this.unitWidth,
30111 y : minY + (this.unitWidth + this.gutter) * 2
30115 x : maxX - this.unitWidth * 3 - this.gutter * 2,
30119 Roo.each(eItems, function(b,k){
30121 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
30127 getVerticalOneBoxColPositions : function(x, y, box)
30131 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
30133 if(box[0].size == 'md-left'){
30137 if(box[0].size == 'md-right'){
30142 x : x + (this.unitWidth + this.gutter) * rand,
30149 getVerticalTwoBoxColPositions : function(x, y, box)
30153 if(box[0].size == 'xs'){
30157 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
30161 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
30175 x : x + (this.unitWidth + this.gutter) * 2,
30176 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
30183 getVerticalThreeBoxColPositions : function(x, y, box)
30187 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30195 x : x + (this.unitWidth + this.gutter) * 1,
30200 x : x + (this.unitWidth + this.gutter) * 2,
30208 if(box[0].size == 'xs' && box[1].size == 'xs'){
30217 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
30221 x : x + (this.unitWidth + this.gutter) * 1,
30235 x : x + (this.unitWidth + this.gutter) * 2,
30240 x : x + (this.unitWidth + this.gutter) * 2,
30241 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
30248 getVerticalFourBoxColPositions : function(x, y, box)
30252 if(box[0].size == 'xs'){
30261 y : y + (this.unitHeight + this.gutter) * 1
30266 y : y + (this.unitHeight + this.gutter) * 2
30270 x : x + (this.unitWidth + this.gutter) * 1,
30284 x : x + (this.unitWidth + this.gutter) * 2,
30289 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
30290 y : y + (this.unitHeight + this.gutter) * 1
30294 x : x + (this.unitWidth + this.gutter) * 2,
30295 y : y + (this.unitWidth + this.gutter) * 2
30302 getHorizontalOneBoxColPositions : function(maxX, minY, box)
30306 if(box[0].size == 'md-left'){
30308 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30315 if(box[0].size == 'md-right'){
30317 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30318 y : minY + (this.unitWidth + this.gutter) * 1
30324 var rand = Math.floor(Math.random() * (4 - box[0].y));
30327 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30328 y : minY + (this.unitWidth + this.gutter) * rand
30335 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
30339 if(box[0].size == 'xs'){
30342 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30347 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30348 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
30356 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30361 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30362 y : minY + (this.unitWidth + this.gutter) * 2
30369 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30373 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30376 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30381 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30382 y : minY + (this.unitWidth + this.gutter) * 1
30386 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30387 y : minY + (this.unitWidth + this.gutter) * 2
30394 if(box[0].size == 'xs' && box[1].size == 'xs'){
30397 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30402 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30407 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30408 y : minY + (this.unitWidth + this.gutter) * 1
30416 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30421 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30422 y : minY + (this.unitWidth + this.gutter) * 2
30426 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30427 y : minY + (this.unitWidth + this.gutter) * 2
30434 getHorizontalFourBoxColPositions : function(maxX, minY, box)
30438 if(box[0].size == 'xs'){
30441 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30446 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30451 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),
30456 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30457 y : minY + (this.unitWidth + this.gutter) * 1
30465 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30470 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30471 y : minY + (this.unitWidth + this.gutter) * 2
30475 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30476 y : minY + (this.unitWidth + this.gutter) * 2
30480 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),
30481 y : minY + (this.unitWidth + this.gutter) * 2
30495 * http://masonry.desandro.com
30497 * The idea is to render all the bricks based on vertical width...
30499 * The original code extends 'outlayer' - we might need to use that....
30505 * @class Roo.bootstrap.LayoutMasonryAuto
30506 * @extends Roo.bootstrap.Component
30507 * Bootstrap Layout Masonry class
30510 * Create a new Element
30511 * @param {Object} config The config object
30514 Roo.bootstrap.LayoutMasonryAuto = function(config){
30515 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30518 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
30521 * @cfg {Boolean} isFitWidth - resize the width..
30523 isFitWidth : false, // options..
30525 * @cfg {Boolean} isOriginLeft = left align?
30527 isOriginLeft : true,
30529 * @cfg {Boolean} isOriginTop = top align?
30531 isOriginTop : false,
30533 * @cfg {Boolean} isLayoutInstant = no animation?
30535 isLayoutInstant : false, // needed?
30537 * @cfg {Boolean} isResizingContainer = not sure if this is used..
30539 isResizingContainer : true,
30541 * @cfg {Number} columnWidth width of the columns
30547 * @cfg {Number} maxCols maximum number of columns
30552 * @cfg {Number} padHeight padding below box..
30558 * @cfg {Boolean} isAutoInitial defalut true
30561 isAutoInitial : true,
30567 initialColumnWidth : 0,
30568 currentSize : null,
30570 colYs : null, // array.
30577 bricks: null, //CompositeElement
30578 cols : 0, // array?
30579 // element : null, // wrapped now this.el
30580 _isLayoutInited : null,
30583 getAutoCreate : function(){
30587 cls: 'blog-masonary-wrapper ' + this.cls,
30589 cls : 'mas-boxes masonary'
30596 getChildContainer: function( )
30598 if (this.boxesEl) {
30599 return this.boxesEl;
30602 this.boxesEl = this.el.select('.mas-boxes').first();
30604 return this.boxesEl;
30608 initEvents : function()
30612 if(this.isAutoInitial){
30613 Roo.log('hook children rendered');
30614 this.on('childrenrendered', function() {
30615 Roo.log('children rendered');
30622 initial : function()
30624 this.reloadItems();
30626 this.currentSize = this.el.getBox(true);
30628 /// was window resize... - let's see if this works..
30629 Roo.EventManager.onWindowResize(this.resize, this);
30631 if(!this.isAutoInitial){
30636 this.layout.defer(500,this);
30639 reloadItems: function()
30641 this.bricks = this.el.select('.masonry-brick', true);
30643 this.bricks.each(function(b) {
30644 //Roo.log(b.getSize());
30645 if (!b.attr('originalwidth')) {
30646 b.attr('originalwidth', b.getSize().width);
30651 Roo.log(this.bricks.elements.length);
30654 resize : function()
30657 var cs = this.el.getBox(true);
30659 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30660 Roo.log("no change in with or X");
30663 this.currentSize = cs;
30667 layout : function()
30670 this._resetLayout();
30671 //this._manageStamps();
30673 // don't animate first layout
30674 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30675 this.layoutItems( isInstant );
30677 // flag for initalized
30678 this._isLayoutInited = true;
30681 layoutItems : function( isInstant )
30683 //var items = this._getItemsForLayout( this.items );
30684 // original code supports filtering layout items.. we just ignore it..
30686 this._layoutItems( this.bricks , isInstant );
30688 this._postLayout();
30690 _layoutItems : function ( items , isInstant)
30692 //this.fireEvent( 'layout', this, items );
30695 if ( !items || !items.elements.length ) {
30696 // no items, emit event with empty array
30701 items.each(function(item) {
30702 Roo.log("layout item");
30704 // get x/y object from method
30705 var position = this._getItemLayoutPosition( item );
30707 position.item = item;
30708 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30709 queue.push( position );
30712 this._processLayoutQueue( queue );
30714 /** Sets position of item in DOM
30715 * @param {Element} item
30716 * @param {Number} x - horizontal position
30717 * @param {Number} y - vertical position
30718 * @param {Boolean} isInstant - disables transitions
30720 _processLayoutQueue : function( queue )
30722 for ( var i=0, len = queue.length; i < len; i++ ) {
30723 var obj = queue[i];
30724 obj.item.position('absolute');
30725 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30731 * Any logic you want to do after each layout,
30732 * i.e. size the container
30734 _postLayout : function()
30736 this.resizeContainer();
30739 resizeContainer : function()
30741 if ( !this.isResizingContainer ) {
30744 var size = this._getContainerSize();
30746 this.el.setSize(size.width,size.height);
30747 this.boxesEl.setSize(size.width,size.height);
30753 _resetLayout : function()
30755 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30756 this.colWidth = this.el.getWidth();
30757 //this.gutter = this.el.getWidth();
30759 this.measureColumns();
30765 this.colYs.push( 0 );
30771 measureColumns : function()
30773 this.getContainerWidth();
30774 // if columnWidth is 0, default to outerWidth of first item
30775 if ( !this.columnWidth ) {
30776 var firstItem = this.bricks.first();
30777 Roo.log(firstItem);
30778 this.columnWidth = this.containerWidth;
30779 if (firstItem && firstItem.attr('originalwidth') ) {
30780 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30782 // columnWidth fall back to item of first element
30783 Roo.log("set column width?");
30784 this.initialColumnWidth = this.columnWidth ;
30786 // if first elem has no width, default to size of container
30791 if (this.initialColumnWidth) {
30792 this.columnWidth = this.initialColumnWidth;
30797 // column width is fixed at the top - however if container width get's smaller we should
30800 // this bit calcs how man columns..
30802 var columnWidth = this.columnWidth += this.gutter;
30804 // calculate columns
30805 var containerWidth = this.containerWidth + this.gutter;
30807 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30808 // fix rounding errors, typically with gutters
30809 var excess = columnWidth - containerWidth % columnWidth;
30812 // if overshoot is less than a pixel, round up, otherwise floor it
30813 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30814 cols = Math[ mathMethod ]( cols );
30815 this.cols = Math.max( cols, 1 );
30816 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30818 // padding positioning..
30819 var totalColWidth = this.cols * this.columnWidth;
30820 var padavail = this.containerWidth - totalColWidth;
30821 // so for 2 columns - we need 3 'pads'
30823 var padNeeded = (1+this.cols) * this.padWidth;
30825 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30827 this.columnWidth += padExtra
30828 //this.padWidth = Math.floor(padavail / ( this.cols));
30830 // adjust colum width so that padding is fixed??
30832 // we have 3 columns ... total = width * 3
30833 // we have X left over... that should be used by
30835 //if (this.expandC) {
30843 getContainerWidth : function()
30845 /* // container is parent if fit width
30846 var container = this.isFitWidth ? this.element.parentNode : this.element;
30847 // check that this.size and size are there
30848 // IE8 triggers resize on body size change, so they might not be
30850 var size = getSize( container ); //FIXME
30851 this.containerWidth = size && size.innerWidth; //FIXME
30854 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30858 _getItemLayoutPosition : function( item ) // what is item?
30860 // we resize the item to our columnWidth..
30862 item.setWidth(this.columnWidth);
30863 item.autoBoxAdjust = false;
30865 var sz = item.getSize();
30867 // how many columns does this brick span
30868 var remainder = this.containerWidth % this.columnWidth;
30870 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30871 // round if off by 1 pixel, otherwise use ceil
30872 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
30873 colSpan = Math.min( colSpan, this.cols );
30875 // normally this should be '1' as we dont' currently allow multi width columns..
30877 var colGroup = this._getColGroup( colSpan );
30878 // get the minimum Y value from the columns
30879 var minimumY = Math.min.apply( Math, colGroup );
30880 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30882 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
30884 // position the brick
30886 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30887 y: this.currentSize.y + minimumY + this.padHeight
30891 // apply setHeight to necessary columns
30892 var setHeight = minimumY + sz.height + this.padHeight;
30893 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30895 var setSpan = this.cols + 1 - colGroup.length;
30896 for ( var i = 0; i < setSpan; i++ ) {
30897 this.colYs[ shortColIndex + i ] = setHeight ;
30904 * @param {Number} colSpan - number of columns the element spans
30905 * @returns {Array} colGroup
30907 _getColGroup : function( colSpan )
30909 if ( colSpan < 2 ) {
30910 // if brick spans only one column, use all the column Ys
30915 // how many different places could this brick fit horizontally
30916 var groupCount = this.cols + 1 - colSpan;
30917 // for each group potential horizontal position
30918 for ( var i = 0; i < groupCount; i++ ) {
30919 // make an array of colY values for that one group
30920 var groupColYs = this.colYs.slice( i, i + colSpan );
30921 // and get the max value of the array
30922 colGroup[i] = Math.max.apply( Math, groupColYs );
30927 _manageStamp : function( stamp )
30929 var stampSize = stamp.getSize();
30930 var offset = stamp.getBox();
30931 // get the columns that this stamp affects
30932 var firstX = this.isOriginLeft ? offset.x : offset.right;
30933 var lastX = firstX + stampSize.width;
30934 var firstCol = Math.floor( firstX / this.columnWidth );
30935 firstCol = Math.max( 0, firstCol );
30937 var lastCol = Math.floor( lastX / this.columnWidth );
30938 // lastCol should not go over if multiple of columnWidth #425
30939 lastCol -= lastX % this.columnWidth ? 0 : 1;
30940 lastCol = Math.min( this.cols - 1, lastCol );
30942 // set colYs to bottom of the stamp
30943 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30946 for ( var i = firstCol; i <= lastCol; i++ ) {
30947 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30952 _getContainerSize : function()
30954 this.maxY = Math.max.apply( Math, this.colYs );
30959 if ( this.isFitWidth ) {
30960 size.width = this._getContainerFitWidth();
30966 _getContainerFitWidth : function()
30968 var unusedCols = 0;
30969 // count unused columns
30972 if ( this.colYs[i] !== 0 ) {
30977 // fit container to columns that have been used
30978 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30981 needsResizeLayout : function()
30983 var previousWidth = this.containerWidth;
30984 this.getContainerWidth();
30985 return previousWidth !== this.containerWidth;
31000 * @class Roo.bootstrap.MasonryBrick
31001 * @extends Roo.bootstrap.Component
31002 * Bootstrap MasonryBrick class
31005 * Create a new MasonryBrick
31006 * @param {Object} config The config object
31009 Roo.bootstrap.MasonryBrick = function(config){
31010 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
31016 * When a MasonryBrick is clcik
31017 * @param {Roo.bootstrap.MasonryBrick} this
31018 * @param {Roo.EventObject} e
31024 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
31027 * @cfg {String} title
31031 * @cfg {String} html
31035 * @cfg {String} bgimage
31039 * @cfg {String} videourl
31043 * @cfg {String} cls
31047 * @cfg {String} href
31051 * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
31056 * @cfg {String} (center|bottom) placetitle
31061 * @cfg {Boolean} isFitContainer defalut true
31063 isFitContainer : true,
31066 * @cfg {Boolean} preventDefault defalut false
31068 preventDefault : false,
31070 getAutoCreate : function()
31072 if(!this.isFitContainer){
31073 return this.getSplitAutoCreate();
31076 var cls = 'masonry-brick masonry-brick-full';
31078 if(this.href.length){
31079 cls += ' masonry-brick-link';
31082 if(this.bgimage.length){
31083 cls += ' masonry-brick-image';
31086 if(!this.html.length){
31087 cls += ' enable-mask';
31091 cls += ' masonry-' + this.size + '-brick';
31094 if(this.placetitle.length){
31096 switch (this.placetitle) {
31098 cls += ' masonry-center-title';
31101 cls += ' masonry-bottom-title';
31108 if(!this.html.length && !this.bgimage.length){
31109 cls += ' masonry-center-title';
31112 if(!this.html.length && this.bgimage.length){
31113 cls += ' masonry-bottom-title';
31118 cls += ' ' + this.cls;
31122 tag: (this.href.length) ? 'a' : 'div',
31127 cls: 'masonry-brick-paragraph',
31133 if(this.href.length){
31134 cfg.href = this.href;
31137 var cn = cfg.cn[0].cn;
31139 if(this.title.length){
31142 cls: 'masonry-brick-title',
31147 if(this.html.length){
31150 cls: 'masonry-brick-text',
31154 if (!this.title.length && !this.html.length) {
31155 cfg.cn[0].cls += ' hide';
31158 if(this.bgimage.length){
31161 cls: 'masonry-brick-image-view',
31166 if(this.videourl.length){
31167 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31168 // youtube support only?
31171 cls: 'masonry-brick-image-view',
31174 allowfullscreen : true
31182 cls: 'masonry-brick-mask'
31189 getSplitAutoCreate : function()
31191 var cls = 'masonry-brick masonry-brick-split';
31193 if(this.href.length){
31194 cls += ' masonry-brick-link';
31197 if(this.bgimage.length){
31198 cls += ' masonry-brick-image';
31202 cls += ' masonry-' + this.size + '-brick';
31205 switch (this.placetitle) {
31207 cls += ' masonry-center-title';
31210 cls += ' masonry-bottom-title';
31213 if(!this.bgimage.length){
31214 cls += ' masonry-center-title';
31217 if(this.bgimage.length){
31218 cls += ' masonry-bottom-title';
31224 cls += ' ' + this.cls;
31228 tag: (this.href.length) ? 'a' : 'div',
31233 cls: 'masonry-brick-split-head',
31237 cls: 'masonry-brick-paragraph',
31244 cls: 'masonry-brick-split-body',
31250 if(this.href.length){
31251 cfg.href = this.href;
31254 if(this.title.length){
31255 cfg.cn[0].cn[0].cn.push({
31257 cls: 'masonry-brick-title',
31262 if(this.html.length){
31263 cfg.cn[1].cn.push({
31265 cls: 'masonry-brick-text',
31270 if(this.bgimage.length){
31271 cfg.cn[0].cn.push({
31273 cls: 'masonry-brick-image-view',
31278 if(this.videourl.length){
31279 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31280 // youtube support only?
31281 cfg.cn[0].cn.cn.push({
31283 cls: 'masonry-brick-image-view',
31286 allowfullscreen : true
31293 initEvents: function()
31295 switch (this.size) {
31328 this.el.on('touchstart', this.onTouchStart, this);
31329 this.el.on('touchmove', this.onTouchMove, this);
31330 this.el.on('touchend', this.onTouchEnd, this);
31331 this.el.on('contextmenu', this.onContextMenu, this);
31333 this.el.on('mouseenter' ,this.enter, this);
31334 this.el.on('mouseleave', this.leave, this);
31335 this.el.on('click', this.onClick, this);
31338 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
31339 this.parent().bricks.push(this);
31344 onClick: function(e, el)
31346 var time = this.endTimer - this.startTimer;
31350 e.preventDefault();
31355 if(!this.preventDefault){
31359 e.preventDefault();
31360 this.fireEvent('click', this);
31363 enter: function(e, el)
31365 e.preventDefault();
31367 if(!this.isFitContainer){
31371 if(this.bgimage.length && this.html.length){
31372 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31376 leave: function(e, el)
31378 e.preventDefault();
31380 if(!this.isFitContainer){
31384 if(this.bgimage.length && this.html.length){
31385 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31389 onTouchStart: function(e, el)
31391 // e.preventDefault();
31393 this.touchmoved = false;
31395 if(!this.isFitContainer){
31399 if(!this.bgimage.length || !this.html.length){
31403 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31405 this.timer = new Date().getTime();
31409 onTouchMove: function(e, el)
31411 this.touchmoved = true;
31414 onContextMenu : function(e,el)
31416 e.preventDefault();
31417 e.stopPropagation();
31421 onTouchEnd: function(e, el)
31423 // e.preventDefault();
31425 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
31432 if(!this.bgimage.length || !this.html.length){
31434 if(this.href.length){
31435 window.location.href = this.href;
31441 if(!this.isFitContainer){
31445 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31447 window.location.href = this.href;
31462 * @class Roo.bootstrap.Brick
31463 * @extends Roo.bootstrap.Component
31464 * Bootstrap Brick class
31467 * Create a new Brick
31468 * @param {Object} config The config object
31471 Roo.bootstrap.Brick = function(config){
31472 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
31478 * When a Brick is click
31479 * @param {Roo.bootstrap.Brick} this
31480 * @param {Roo.EventObject} e
31486 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
31489 * @cfg {String} title
31493 * @cfg {String} html
31497 * @cfg {String} bgimage
31501 * @cfg {String} cls
31505 * @cfg {String} href
31509 * @cfg {String} video
31513 * @cfg {Boolean} square
31517 getAutoCreate : function()
31519 var cls = 'roo-brick';
31521 if(this.href.length){
31522 cls += ' roo-brick-link';
31525 if(this.bgimage.length){
31526 cls += ' roo-brick-image';
31529 if(!this.html.length && !this.bgimage.length){
31530 cls += ' roo-brick-center-title';
31533 if(!this.html.length && this.bgimage.length){
31534 cls += ' roo-brick-bottom-title';
31538 cls += ' ' + this.cls;
31542 tag: (this.href.length) ? 'a' : 'div',
31547 cls: 'roo-brick-paragraph',
31553 if(this.href.length){
31554 cfg.href = this.href;
31557 var cn = cfg.cn[0].cn;
31559 if(this.title.length){
31562 cls: 'roo-brick-title',
31567 if(this.html.length){
31570 cls: 'roo-brick-text',
31577 if(this.bgimage.length){
31580 cls: 'roo-brick-image-view',
31588 initEvents: function()
31590 if(this.title.length || this.html.length){
31591 this.el.on('mouseenter' ,this.enter, this);
31592 this.el.on('mouseleave', this.leave, this);
31596 Roo.EventManager.onWindowResize(this.resize, this);
31601 resize : function()
31603 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
31605 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
31607 if(this.bgimage.length){
31608 var image = this.el.select('.roo-brick-image-view', true).first();
31609 image.setWidth(paragraph.getWidth());
31610 image.setHeight(paragraph.getWidth());
31612 this.el.setHeight(paragraph.getWidth());
31618 enter: function(e, el)
31620 e.preventDefault();
31622 if(this.bgimage.length){
31623 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
31624 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
31628 leave: function(e, el)
31630 e.preventDefault();
31632 if(this.bgimage.length){
31633 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
31634 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
31650 * @class Roo.bootstrap.NumberField
31651 * @extends Roo.bootstrap.Input
31652 * Bootstrap NumberField class
31658 * Create a new NumberField
31659 * @param {Object} config The config object
31662 Roo.bootstrap.NumberField = function(config){
31663 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
31666 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
31669 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
31671 allowDecimals : true,
31673 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
31675 decimalSeparator : ".",
31677 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
31679 decimalPrecision : 2,
31681 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
31683 allowNegative : true,
31685 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
31687 minValue : Number.NEGATIVE_INFINITY,
31689 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
31691 maxValue : Number.MAX_VALUE,
31693 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
31695 minText : "The minimum value for this field is {0}",
31697 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
31699 maxText : "The maximum value for this field is {0}",
31701 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
31702 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
31704 nanText : "{0} is not a valid number",
31706 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
31711 initEvents : function()
31713 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
31715 var allowed = "0123456789";
31717 if(this.allowDecimals){
31718 allowed += this.decimalSeparator;
31721 if(this.allowNegative){
31725 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
31727 var keyPress = function(e){
31729 var k = e.getKey();
31731 var c = e.getCharCode();
31734 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
31735 allowed.indexOf(String.fromCharCode(c)) === -1
31741 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
31745 if(allowed.indexOf(String.fromCharCode(c)) === -1){
31750 this.el.on("keypress", keyPress, this);
31753 validateValue : function(value)
31756 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
31760 var num = this.parseValue(value);
31763 this.markInvalid(String.format(this.nanText, value));
31767 if(num < this.minValue){
31768 this.markInvalid(String.format(this.minText, this.minValue));
31772 if(num > this.maxValue){
31773 this.markInvalid(String.format(this.maxText, this.maxValue));
31780 getValue : function()
31782 return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
31785 parseValue : function(value)
31787 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
31788 return isNaN(value) ? '' : value;
31791 fixPrecision : function(value)
31793 var nan = isNaN(value);
31795 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
31796 return nan ? '' : value;
31798 return parseFloat(value).toFixed(this.decimalPrecision);
31801 setValue : function(v)
31803 v = this.fixPrecision(v);
31804 Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
31807 decimalPrecisionFcn : function(v)
31809 return Math.floor(v);
31812 beforeBlur : function()
31818 var v = this.parseValue(this.getRawValue());
31833 * @class Roo.bootstrap.DocumentSlider
31834 * @extends Roo.bootstrap.Component
31835 * Bootstrap DocumentSlider class
31838 * Create a new DocumentViewer
31839 * @param {Object} config The config object
31842 Roo.bootstrap.DocumentSlider = function(config){
31843 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
31850 * Fire after initEvent
31851 * @param {Roo.bootstrap.DocumentSlider} this
31856 * Fire after update
31857 * @param {Roo.bootstrap.DocumentSlider} this
31863 * @param {Roo.bootstrap.DocumentSlider} this
31869 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
31875 getAutoCreate : function()
31879 cls : 'roo-document-slider',
31883 cls : 'roo-document-slider-header',
31887 cls : 'roo-document-slider-header-title'
31893 cls : 'roo-document-slider-body',
31897 cls : 'roo-document-slider-prev',
31901 cls : 'fa fa-chevron-left'
31907 cls : 'roo-document-slider-thumb',
31911 cls : 'roo-document-slider-image'
31917 cls : 'roo-document-slider-next',
31921 cls : 'fa fa-chevron-right'
31933 initEvents : function()
31935 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
31936 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
31938 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
31939 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
31941 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
31942 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31944 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
31945 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31947 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
31948 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31950 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
31951 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
31953 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
31954 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
31956 this.thumbEl.on('click', this.onClick, this);
31958 this.prevIndicator.on('click', this.prev, this);
31960 this.nextIndicator.on('click', this.next, this);
31964 initial : function()
31966 if(this.files.length){
31967 this.indicator = 1;
31971 this.fireEvent('initial', this);
31974 update : function()
31976 this.imageEl.attr('src', this.files[this.indicator - 1]);
31978 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
31980 this.prevIndicator.show();
31982 if(this.indicator == 1){
31983 this.prevIndicator.hide();
31986 this.nextIndicator.show();
31988 if(this.indicator == this.files.length){
31989 this.nextIndicator.hide();
31992 this.thumbEl.scrollTo('top');
31994 this.fireEvent('update', this);
31997 onClick : function(e)
31999 e.preventDefault();
32001 this.fireEvent('click', this);
32006 e.preventDefault();
32008 this.indicator = Math.max(1, this.indicator - 1);
32015 e.preventDefault();
32017 this.indicator = Math.min(this.files.length, this.indicator + 1);
32024 * Ext JS Library 1.1.1
32025 * Copyright(c) 2006-2007, Ext JS, LLC.
32027 * Originally Released Under LGPL - original licence link has changed is not relivant.
32030 * <script type="text/javascript">
32035 * @class Roo.bootstrap.SplitBar
32036 * @extends Roo.util.Observable
32037 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
32041 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
32042 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
32043 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
32044 split.minSize = 100;
32045 split.maxSize = 600;
32046 split.animate = true;
32047 split.on('moved', splitterMoved);
32050 * Create a new SplitBar
32051 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
32052 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
32053 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32054 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
32055 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
32056 position of the SplitBar).
32058 Roo.bootstrap.SplitBar = function(cfg){
32063 // dragElement : elm
32064 // resizingElement: el,
32066 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
32067 // placement : Roo.bootstrap.SplitBar.LEFT ,
32068 // existingProxy ???
32071 this.el = Roo.get(cfg.dragElement, true);
32072 this.el.dom.unselectable = "on";
32074 this.resizingEl = Roo.get(cfg.resizingElement, true);
32078 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32079 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
32082 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
32085 * The minimum size of the resizing element. (Defaults to 0)
32091 * The maximum size of the resizing element. (Defaults to 2000)
32094 this.maxSize = 2000;
32097 * Whether to animate the transition to the new size
32100 this.animate = false;
32103 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
32106 this.useShim = false;
32111 if(!cfg.existingProxy){
32113 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
32115 this.proxy = Roo.get(cfg.existingProxy).dom;
32118 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
32121 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
32124 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
32127 this.dragSpecs = {};
32130 * @private The adapter to use to positon and resize elements
32132 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32133 this.adapter.init(this);
32135 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32137 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
32138 this.el.addClass("roo-splitbar-h");
32141 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
32142 this.el.addClass("roo-splitbar-v");
32148 * Fires when the splitter is moved (alias for {@link #event-moved})
32149 * @param {Roo.bootstrap.SplitBar} this
32150 * @param {Number} newSize the new width or height
32155 * Fires when the splitter is moved
32156 * @param {Roo.bootstrap.SplitBar} this
32157 * @param {Number} newSize the new width or height
32161 * @event beforeresize
32162 * Fires before the splitter is dragged
32163 * @param {Roo.bootstrap.SplitBar} this
32165 "beforeresize" : true,
32167 "beforeapply" : true
32170 Roo.util.Observable.call(this);
32173 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
32174 onStartProxyDrag : function(x, y){
32175 this.fireEvent("beforeresize", this);
32177 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
32179 o.enableDisplayMode("block");
32180 // all splitbars share the same overlay
32181 Roo.bootstrap.SplitBar.prototype.overlay = o;
32183 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32184 this.overlay.show();
32185 Roo.get(this.proxy).setDisplayed("block");
32186 var size = this.adapter.getElementSize(this);
32187 this.activeMinSize = this.getMinimumSize();;
32188 this.activeMaxSize = this.getMaximumSize();;
32189 var c1 = size - this.activeMinSize;
32190 var c2 = Math.max(this.activeMaxSize - size, 0);
32191 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32192 this.dd.resetConstraints();
32193 this.dd.setXConstraint(
32194 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
32195 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
32197 this.dd.setYConstraint(0, 0);
32199 this.dd.resetConstraints();
32200 this.dd.setXConstraint(0, 0);
32201 this.dd.setYConstraint(
32202 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
32203 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
32206 this.dragSpecs.startSize = size;
32207 this.dragSpecs.startPoint = [x, y];
32208 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
32212 * @private Called after the drag operation by the DDProxy
32214 onEndProxyDrag : function(e){
32215 Roo.get(this.proxy).setDisplayed(false);
32216 var endPoint = Roo.lib.Event.getXY(e);
32218 this.overlay.hide();
32221 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32222 newSize = this.dragSpecs.startSize +
32223 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
32224 endPoint[0] - this.dragSpecs.startPoint[0] :
32225 this.dragSpecs.startPoint[0] - endPoint[0]
32228 newSize = this.dragSpecs.startSize +
32229 (this.placement == Roo.bootstrap.SplitBar.TOP ?
32230 endPoint[1] - this.dragSpecs.startPoint[1] :
32231 this.dragSpecs.startPoint[1] - endPoint[1]
32234 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
32235 if(newSize != this.dragSpecs.startSize){
32236 if(this.fireEvent('beforeapply', this, newSize) !== false){
32237 this.adapter.setElementSize(this, newSize);
32238 this.fireEvent("moved", this, newSize);
32239 this.fireEvent("resize", this, newSize);
32245 * Get the adapter this SplitBar uses
32246 * @return The adapter object
32248 getAdapter : function(){
32249 return this.adapter;
32253 * Set the adapter this SplitBar uses
32254 * @param {Object} adapter A SplitBar adapter object
32256 setAdapter : function(adapter){
32257 this.adapter = adapter;
32258 this.adapter.init(this);
32262 * Gets the minimum size for the resizing element
32263 * @return {Number} The minimum size
32265 getMinimumSize : function(){
32266 return this.minSize;
32270 * Sets the minimum size for the resizing element
32271 * @param {Number} minSize The minimum size
32273 setMinimumSize : function(minSize){
32274 this.minSize = minSize;
32278 * Gets the maximum size for the resizing element
32279 * @return {Number} The maximum size
32281 getMaximumSize : function(){
32282 return this.maxSize;
32286 * Sets the maximum size for the resizing element
32287 * @param {Number} maxSize The maximum size
32289 setMaximumSize : function(maxSize){
32290 this.maxSize = maxSize;
32294 * Sets the initialize size for the resizing element
32295 * @param {Number} size The initial size
32297 setCurrentSize : function(size){
32298 var oldAnimate = this.animate;
32299 this.animate = false;
32300 this.adapter.setElementSize(this, size);
32301 this.animate = oldAnimate;
32305 * Destroy this splitbar.
32306 * @param {Boolean} removeEl True to remove the element
32308 destroy : function(removeEl){
32310 this.shim.remove();
32313 this.proxy.parentNode.removeChild(this.proxy);
32321 * @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.
32323 Roo.bootstrap.SplitBar.createProxy = function(dir){
32324 var proxy = new Roo.Element(document.createElement("div"));
32325 proxy.unselectable();
32326 var cls = 'roo-splitbar-proxy';
32327 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
32328 document.body.appendChild(proxy.dom);
32333 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
32334 * Default Adapter. It assumes the splitter and resizing element are not positioned
32335 * elements and only gets/sets the width of the element. Generally used for table based layouts.
32337 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
32340 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
32341 // do nothing for now
32342 init : function(s){
32346 * Called before drag operations to get the current size of the resizing element.
32347 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32349 getElementSize : function(s){
32350 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32351 return s.resizingEl.getWidth();
32353 return s.resizingEl.getHeight();
32358 * Called after drag operations to set the size of the resizing element.
32359 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32360 * @param {Number} newSize The new size to set
32361 * @param {Function} onComplete A function to be invoked when resizing is complete
32363 setElementSize : function(s, newSize, onComplete){
32364 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32366 s.resizingEl.setWidth(newSize);
32368 onComplete(s, newSize);
32371 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
32376 s.resizingEl.setHeight(newSize);
32378 onComplete(s, newSize);
32381 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
32388 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
32389 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
32390 * Adapter that moves the splitter element to align with the resized sizing element.
32391 * Used with an absolute positioned SplitBar.
32392 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
32393 * document.body, make sure you assign an id to the body element.
32395 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
32396 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32397 this.container = Roo.get(container);
32400 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
32401 init : function(s){
32402 this.basic.init(s);
32405 getElementSize : function(s){
32406 return this.basic.getElementSize(s);
32409 setElementSize : function(s, newSize, onComplete){
32410 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
32413 moveSplitter : function(s){
32414 var yes = Roo.bootstrap.SplitBar;
32415 switch(s.placement){
32417 s.el.setX(s.resizingEl.getRight());
32420 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
32423 s.el.setY(s.resizingEl.getBottom());
32426 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
32433 * Orientation constant - Create a vertical SplitBar
32437 Roo.bootstrap.SplitBar.VERTICAL = 1;
32440 * Orientation constant - Create a horizontal SplitBar
32444 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
32447 * Placement constant - The resizing element is to the left of the splitter element
32451 Roo.bootstrap.SplitBar.LEFT = 1;
32454 * Placement constant - The resizing element is to the right of the splitter element
32458 Roo.bootstrap.SplitBar.RIGHT = 2;
32461 * Placement constant - The resizing element is positioned above the splitter element
32465 Roo.bootstrap.SplitBar.TOP = 3;
32468 * Placement constant - The resizing element is positioned under splitter element
32472 Roo.bootstrap.SplitBar.BOTTOM = 4;
32473 Roo.namespace("Roo.bootstrap.layout");/*
32475 * Ext JS Library 1.1.1
32476 * Copyright(c) 2006-2007, Ext JS, LLC.
32478 * Originally Released Under LGPL - original licence link has changed is not relivant.
32481 * <script type="text/javascript">
32485 * @class Roo.bootstrap.layout.Manager
32486 * @extends Roo.bootstrap.Component
32487 * Base class for layout managers.
32489 Roo.bootstrap.layout.Manager = function(config)
32491 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
32497 /** false to disable window resize monitoring @type Boolean */
32498 this.monitorWindowResize = true;
32503 * Fires when a layout is performed.
32504 * @param {Roo.LayoutManager} this
32508 * @event regionresized
32509 * Fires when the user resizes a region.
32510 * @param {Roo.LayoutRegion} region The resized region
32511 * @param {Number} newSize The new size (width for east/west, height for north/south)
32513 "regionresized" : true,
32515 * @event regioncollapsed
32516 * Fires when a region is collapsed.
32517 * @param {Roo.LayoutRegion} region The collapsed region
32519 "regioncollapsed" : true,
32521 * @event regionexpanded
32522 * Fires when a region is expanded.
32523 * @param {Roo.LayoutRegion} region The expanded region
32525 "regionexpanded" : true
32527 this.updating = false;
32530 this.el = Roo.get(config.el);
32536 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
32541 monitorWindowResize : true,
32547 onRender : function(ct, position)
32550 this.el = Roo.get(ct);
32553 //this.fireEvent('render',this);
32557 initEvents: function()
32561 // ie scrollbar fix
32562 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32563 document.body.scroll = "no";
32564 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32565 this.el.position('relative');
32567 this.id = this.el.id;
32568 this.el.addClass("roo-layout-container");
32569 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32570 if(this.el.dom != document.body ) {
32571 this.el.on('resize', this.layout,this);
32572 this.el.on('show', this.layout,this);
32578 * Returns true if this layout is currently being updated
32579 * @return {Boolean}
32581 isUpdating : function(){
32582 return this.updating;
32586 * Suspend the LayoutManager from doing auto-layouts while
32587 * making multiple add or remove calls
32589 beginUpdate : function(){
32590 this.updating = true;
32594 * Restore auto-layouts and optionally disable the manager from performing a layout
32595 * @param {Boolean} noLayout true to disable a layout update
32597 endUpdate : function(noLayout){
32598 this.updating = false;
32604 layout: function(){
32608 onRegionResized : function(region, newSize){
32609 this.fireEvent("regionresized", region, newSize);
32613 onRegionCollapsed : function(region){
32614 this.fireEvent("regioncollapsed", region);
32617 onRegionExpanded : function(region){
32618 this.fireEvent("regionexpanded", region);
32622 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32623 * performs box-model adjustments.
32624 * @return {Object} The size as an object {width: (the width), height: (the height)}
32626 getViewSize : function()
32629 if(this.el.dom != document.body){
32630 size = this.el.getSize();
32632 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32634 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32635 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32640 * Returns the Element this layout is bound to.
32641 * @return {Roo.Element}
32643 getEl : function(){
32648 * Returns the specified region.
32649 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32650 * @return {Roo.LayoutRegion}
32652 getRegion : function(target){
32653 return this.regions[target.toLowerCase()];
32656 onWindowResize : function(){
32657 if(this.monitorWindowResize){
32664 * Ext JS Library 1.1.1
32665 * Copyright(c) 2006-2007, Ext JS, LLC.
32667 * Originally Released Under LGPL - original licence link has changed is not relivant.
32670 * <script type="text/javascript">
32673 * @class Roo.bootstrap.layout.Border
32674 * @extends Roo.bootstrap.layout.Manager
32675 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32676 * please see: examples/bootstrap/nested.html<br><br>
32678 <b>The container the layout is rendered into can be either the body element or any other element.
32679 If it is not the body element, the container needs to either be an absolute positioned element,
32680 or you will need to add "position:relative" to the css of the container. You will also need to specify
32681 the container size if it is not the body element.</b>
32684 * Create a new Border
32685 * @param {Object} config Configuration options
32687 Roo.bootstrap.layout.Border = function(config){
32688 config = config || {};
32689 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
32693 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32694 if(config[region]){
32695 config[region].region = region;
32696 this.addRegion(config[region]);
32702 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
32704 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
32706 * Creates and adds a new region if it doesn't already exist.
32707 * @param {String} target The target region key (north, south, east, west or center).
32708 * @param {Object} config The regions config object
32709 * @return {BorderLayoutRegion} The new region
32711 addRegion : function(config)
32713 if(!this.regions[config.region]){
32714 var r = this.factory(config);
32715 this.bindRegion(r);
32717 return this.regions[config.region];
32721 bindRegion : function(r){
32722 this.regions[r.config.region] = r;
32724 r.on("visibilitychange", this.layout, this);
32725 r.on("paneladded", this.layout, this);
32726 r.on("panelremoved", this.layout, this);
32727 r.on("invalidated", this.layout, this);
32728 r.on("resized", this.onRegionResized, this);
32729 r.on("collapsed", this.onRegionCollapsed, this);
32730 r.on("expanded", this.onRegionExpanded, this);
32734 * Performs a layout update.
32736 layout : function()
32738 if(this.updating) {
32742 // render all the rebions if they have not been done alreayd?
32743 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32744 if(this.regions[region] && !this.regions[region].bodyEl){
32745 this.regions[region].onRender(this.el)
32749 var size = this.getViewSize();
32750 var w = size.width;
32751 var h = size.height;
32756 //var x = 0, y = 0;
32758 var rs = this.regions;
32759 var north = rs["north"];
32760 var south = rs["south"];
32761 var west = rs["west"];
32762 var east = rs["east"];
32763 var center = rs["center"];
32764 //if(this.hideOnLayout){ // not supported anymore
32765 //c.el.setStyle("display", "none");
32767 if(north && north.isVisible()){
32768 var b = north.getBox();
32769 var m = north.getMargins();
32770 b.width = w - (m.left+m.right);
32773 centerY = b.height + b.y + m.bottom;
32774 centerH -= centerY;
32775 north.updateBox(this.safeBox(b));
32777 if(south && south.isVisible()){
32778 var b = south.getBox();
32779 var m = south.getMargins();
32780 b.width = w - (m.left+m.right);
32782 var totalHeight = (b.height + m.top + m.bottom);
32783 b.y = h - totalHeight + m.top;
32784 centerH -= totalHeight;
32785 south.updateBox(this.safeBox(b));
32787 if(west && west.isVisible()){
32788 var b = west.getBox();
32789 var m = west.getMargins();
32790 b.height = centerH - (m.top+m.bottom);
32792 b.y = centerY + m.top;
32793 var totalWidth = (b.width + m.left + m.right);
32794 centerX += totalWidth;
32795 centerW -= totalWidth;
32796 west.updateBox(this.safeBox(b));
32798 if(east && east.isVisible()){
32799 var b = east.getBox();
32800 var m = east.getMargins();
32801 b.height = centerH - (m.top+m.bottom);
32802 var totalWidth = (b.width + m.left + m.right);
32803 b.x = w - totalWidth + m.left;
32804 b.y = centerY + m.top;
32805 centerW -= totalWidth;
32806 east.updateBox(this.safeBox(b));
32809 var m = center.getMargins();
32811 x: centerX + m.left,
32812 y: centerY + m.top,
32813 width: centerW - (m.left+m.right),
32814 height: centerH - (m.top+m.bottom)
32816 //if(this.hideOnLayout){
32817 //center.el.setStyle("display", "block");
32819 center.updateBox(this.safeBox(centerBox));
32822 this.fireEvent("layout", this);
32826 safeBox : function(box){
32827 box.width = Math.max(0, box.width);
32828 box.height = Math.max(0, box.height);
32833 * Adds a ContentPanel (or subclass) to this layout.
32834 * @param {String} target The target region key (north, south, east, west or center).
32835 * @param {Roo.ContentPanel} panel The panel to add
32836 * @return {Roo.ContentPanel} The added panel
32838 add : function(target, panel){
32840 target = target.toLowerCase();
32841 return this.regions[target].add(panel);
32845 * Remove a ContentPanel (or subclass) to this layout.
32846 * @param {String} target The target region key (north, south, east, west or center).
32847 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32848 * @return {Roo.ContentPanel} The removed panel
32850 remove : function(target, panel){
32851 target = target.toLowerCase();
32852 return this.regions[target].remove(panel);
32856 * Searches all regions for a panel with the specified id
32857 * @param {String} panelId
32858 * @return {Roo.ContentPanel} The panel or null if it wasn't found
32860 findPanel : function(panelId){
32861 var rs = this.regions;
32862 for(var target in rs){
32863 if(typeof rs[target] != "function"){
32864 var p = rs[target].getPanel(panelId);
32874 * Searches all regions for a panel with the specified id and activates (shows) it.
32875 * @param {String/ContentPanel} panelId The panels id or the panel itself
32876 * @return {Roo.ContentPanel} The shown panel or null
32878 showPanel : function(panelId) {
32879 var rs = this.regions;
32880 for(var target in rs){
32881 var r = rs[target];
32882 if(typeof r != "function"){
32883 if(r.hasPanel(panelId)){
32884 return r.showPanel(panelId);
32892 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32893 * @param {Roo.state.Provider} provider (optional) An alternate state provider
32896 restoreState : function(provider){
32898 provider = Roo.state.Manager;
32900 var sm = new Roo.LayoutStateManager();
32901 sm.init(this, provider);
32907 * Adds a xtype elements to the layout.
32911 xtype : 'ContentPanel',
32918 xtype : 'NestedLayoutPanel',
32924 items : [ ... list of content panels or nested layout panels.. ]
32928 * @param {Object} cfg Xtype definition of item to add.
32930 addxtype : function(cfg)
32932 // basically accepts a pannel...
32933 // can accept a layout region..!?!?
32934 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32937 // theory? children can only be panels??
32939 //if (!cfg.xtype.match(/Panel$/)) {
32944 if (typeof(cfg.region) == 'undefined') {
32945 Roo.log("Failed to add Panel, region was not set");
32949 var region = cfg.region;
32955 xitems = cfg.items;
32962 case 'Content': // ContentPanel (el, cfg)
32963 case 'Scroll': // ContentPanel (el, cfg)
32965 cfg.autoCreate = true;
32966 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32968 // var el = this.el.createChild();
32969 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32972 this.add(region, ret);
32976 case 'TreePanel': // our new panel!
32977 cfg.el = this.el.createChild();
32978 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32979 this.add(region, ret);
32984 // create a new Layout (which is a Border Layout...
32986 var clayout = cfg.layout;
32987 clayout.el = this.el.createChild();
32988 clayout.items = clayout.items || [];
32992 // replace this exitems with the clayout ones..
32993 xitems = clayout.items;
32995 // force background off if it's in center...
32996 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32997 cfg.background = false;
32999 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
33002 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33003 //console.log('adding nested layout panel ' + cfg.toSource());
33004 this.add(region, ret);
33005 nb = {}; /// find first...
33010 // needs grid and region
33012 //var el = this.getRegion(region).el.createChild();
33014 *var el = this.el.createChild();
33015 // create the grid first...
33016 cfg.grid.container = el;
33017 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
33020 if (region == 'center' && this.active ) {
33021 cfg.background = false;
33024 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33026 this.add(region, ret);
33028 if (cfg.background) {
33029 // render grid on panel activation (if panel background)
33030 ret.on('activate', function(gp) {
33031 if (!gp.grid.rendered) {
33032 // gp.grid.render(el);
33036 // cfg.grid.render(el);
33042 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
33043 // it was the old xcomponent building that caused this before.
33044 // espeically if border is the top element in the tree.
33054 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33056 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33057 this.add(region, ret);
33061 throw "Can not add '" + cfg.xtype + "' to Border";
33067 this.beginUpdate();
33071 Roo.each(xitems, function(i) {
33072 region = nb && i.region ? i.region : false;
33074 var add = ret.addxtype(i);
33077 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33078 if (!i.background) {
33079 abn[region] = nb[region] ;
33086 // make the last non-background panel active..
33087 //if (nb) { Roo.log(abn); }
33090 for(var r in abn) {
33091 region = this.getRegion(r);
33093 // tried using nb[r], but it does not work..
33095 region.showPanel(abn[r]);
33106 factory : function(cfg)
33109 var validRegions = Roo.bootstrap.layout.Border.regions;
33111 var target = cfg.region;
33114 var r = Roo.bootstrap.layout;
33118 return new r.North(cfg);
33120 return new r.South(cfg);
33122 return new r.East(cfg);
33124 return new r.West(cfg);
33126 return new r.Center(cfg);
33128 throw 'Layout region "'+target+'" not supported.';
33135 * Ext JS Library 1.1.1
33136 * Copyright(c) 2006-2007, Ext JS, LLC.
33138 * Originally Released Under LGPL - original licence link has changed is not relivant.
33141 * <script type="text/javascript">
33145 * @class Roo.bootstrap.layout.Basic
33146 * @extends Roo.util.Observable
33147 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33148 * and does not have a titlebar, tabs or any other features. All it does is size and position
33149 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33150 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
33151 * @cfg {string} region the region that it inhabits..
33152 * @cfg {bool} skipConfig skip config?
33156 Roo.bootstrap.layout.Basic = function(config){
33158 this.mgr = config.mgr;
33160 this.position = config.region;
33162 var skipConfig = config.skipConfig;
33166 * @scope Roo.BasicLayoutRegion
33170 * @event beforeremove
33171 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33172 * @param {Roo.LayoutRegion} this
33173 * @param {Roo.ContentPanel} panel The panel
33174 * @param {Object} e The cancel event object
33176 "beforeremove" : true,
33178 * @event invalidated
33179 * Fires when the layout for this region is changed.
33180 * @param {Roo.LayoutRegion} this
33182 "invalidated" : true,
33184 * @event visibilitychange
33185 * Fires when this region is shown or hidden
33186 * @param {Roo.LayoutRegion} this
33187 * @param {Boolean} visibility true or false
33189 "visibilitychange" : true,
33191 * @event paneladded
33192 * Fires when a panel is added.
33193 * @param {Roo.LayoutRegion} this
33194 * @param {Roo.ContentPanel} panel The panel
33196 "paneladded" : true,
33198 * @event panelremoved
33199 * Fires when a panel is removed.
33200 * @param {Roo.LayoutRegion} this
33201 * @param {Roo.ContentPanel} panel The panel
33203 "panelremoved" : true,
33205 * @event beforecollapse
33206 * Fires when this region before collapse.
33207 * @param {Roo.LayoutRegion} this
33209 "beforecollapse" : true,
33212 * Fires when this region is collapsed.
33213 * @param {Roo.LayoutRegion} this
33215 "collapsed" : true,
33218 * Fires when this region is expanded.
33219 * @param {Roo.LayoutRegion} this
33224 * Fires when this region is slid into view.
33225 * @param {Roo.LayoutRegion} this
33227 "slideshow" : true,
33230 * Fires when this region slides out of view.
33231 * @param {Roo.LayoutRegion} this
33233 "slidehide" : true,
33235 * @event panelactivated
33236 * Fires when a panel is activated.
33237 * @param {Roo.LayoutRegion} this
33238 * @param {Roo.ContentPanel} panel The activated panel
33240 "panelactivated" : true,
33243 * Fires when the user resizes this region.
33244 * @param {Roo.LayoutRegion} this
33245 * @param {Number} newSize The new size (width for east/west, height for north/south)
33249 /** A collection of panels in this region. @type Roo.util.MixedCollection */
33250 this.panels = new Roo.util.MixedCollection();
33251 this.panels.getKey = this.getPanelId.createDelegate(this);
33253 this.activePanel = null;
33254 // ensure listeners are added...
33256 if (config.listeners || config.events) {
33257 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
33258 listeners : config.listeners || {},
33259 events : config.events || {}
33263 if(skipConfig !== true){
33264 this.applyConfig(config);
33268 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
33270 getPanelId : function(p){
33274 applyConfig : function(config){
33275 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33276 this.config = config;
33281 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
33282 * the width, for horizontal (north, south) the height.
33283 * @param {Number} newSize The new width or height
33285 resizeTo : function(newSize){
33286 var el = this.el ? this.el :
33287 (this.activePanel ? this.activePanel.getEl() : null);
33289 switch(this.position){
33292 el.setWidth(newSize);
33293 this.fireEvent("resized", this, newSize);
33297 el.setHeight(newSize);
33298 this.fireEvent("resized", this, newSize);
33304 getBox : function(){
33305 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33308 getMargins : function(){
33309 return this.margins;
33312 updateBox : function(box){
33314 var el = this.activePanel.getEl();
33315 el.dom.style.left = box.x + "px";
33316 el.dom.style.top = box.y + "px";
33317 this.activePanel.setSize(box.width, box.height);
33321 * Returns the container element for this region.
33322 * @return {Roo.Element}
33324 getEl : function(){
33325 return this.activePanel;
33329 * Returns true if this region is currently visible.
33330 * @return {Boolean}
33332 isVisible : function(){
33333 return this.activePanel ? true : false;
33336 setActivePanel : function(panel){
33337 panel = this.getPanel(panel);
33338 if(this.activePanel && this.activePanel != panel){
33339 this.activePanel.setActiveState(false);
33340 this.activePanel.getEl().setLeftTop(-10000,-10000);
33342 this.activePanel = panel;
33343 panel.setActiveState(true);
33345 panel.setSize(this.box.width, this.box.height);
33347 this.fireEvent("panelactivated", this, panel);
33348 this.fireEvent("invalidated");
33352 * Show the specified panel.
33353 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33354 * @return {Roo.ContentPanel} The shown panel or null
33356 showPanel : function(panel){
33357 panel = this.getPanel(panel);
33359 this.setActivePanel(panel);
33365 * Get the active panel for this region.
33366 * @return {Roo.ContentPanel} The active panel or null
33368 getActivePanel : function(){
33369 return this.activePanel;
33373 * Add the passed ContentPanel(s)
33374 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33375 * @return {Roo.ContentPanel} The panel added (if only one was added)
33377 add : function(panel){
33378 if(arguments.length > 1){
33379 for(var i = 0, len = arguments.length; i < len; i++) {
33380 this.add(arguments[i]);
33384 if(this.hasPanel(panel)){
33385 this.showPanel(panel);
33388 var el = panel.getEl();
33389 if(el.dom.parentNode != this.mgr.el.dom){
33390 this.mgr.el.dom.appendChild(el.dom);
33392 if(panel.setRegion){
33393 panel.setRegion(this);
33395 this.panels.add(panel);
33396 el.setStyle("position", "absolute");
33397 if(!panel.background){
33398 this.setActivePanel(panel);
33399 if(this.config.initialSize && this.panels.getCount()==1){
33400 this.resizeTo(this.config.initialSize);
33403 this.fireEvent("paneladded", this, panel);
33408 * Returns true if the panel is in this region.
33409 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33410 * @return {Boolean}
33412 hasPanel : function(panel){
33413 if(typeof panel == "object"){ // must be panel obj
33414 panel = panel.getId();
33416 return this.getPanel(panel) ? true : false;
33420 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33421 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33422 * @param {Boolean} preservePanel Overrides the config preservePanel option
33423 * @return {Roo.ContentPanel} The panel that was removed
33425 remove : function(panel, preservePanel){
33426 panel = this.getPanel(panel);
33431 this.fireEvent("beforeremove", this, panel, e);
33432 if(e.cancel === true){
33435 var panelId = panel.getId();
33436 this.panels.removeKey(panelId);
33441 * Returns the panel specified or null if it's not in this region.
33442 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33443 * @return {Roo.ContentPanel}
33445 getPanel : function(id){
33446 if(typeof id == "object"){ // must be panel obj
33449 return this.panels.get(id);
33453 * Returns this regions position (north/south/east/west/center).
33456 getPosition: function(){
33457 return this.position;
33461 * Ext JS Library 1.1.1
33462 * Copyright(c) 2006-2007, Ext JS, LLC.
33464 * Originally Released Under LGPL - original licence link has changed is not relivant.
33467 * <script type="text/javascript">
33471 * @class Roo.bootstrap.layout.Region
33472 * @extends Roo.bootstrap.layout.Basic
33473 * This class represents a region in a layout manager.
33475 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33476 * @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})
33477 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
33478 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
33479 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
33480 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
33481 * @cfg {String} title The title for the region (overrides panel titles)
33482 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
33483 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33484 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
33485 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33486 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
33487 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33488 * the space available, similar to FireFox 1.5 tabs (defaults to false)
33489 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
33490 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
33491 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
33493 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
33494 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
33495 * @cfg {Boolean} disableTabTips True to disable tab tooltips
33496 * @cfg {Number} width For East/West panels
33497 * @cfg {Number} height For North/South panels
33498 * @cfg {Boolean} split To show the splitter
33499 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
33501 * @cfg {string} cls Extra CSS classes to add to region
33503 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
33504 * @cfg {string} region the region that it inhabits..
33507 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
33508 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
33510 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
33511 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
33512 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
33514 Roo.bootstrap.layout.Region = function(config)
33516 this.applyConfig(config);
33518 var mgr = config.mgr;
33519 var pos = config.region;
33520 config.skipConfig = true;
33521 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
33524 this.onRender(mgr.el);
33527 this.visible = true;
33528 this.collapsed = false;
33529 this.unrendered_panels = [];
33532 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
33534 position: '', // set by wrapper (eg. north/south etc..)
33535 unrendered_panels : null, // unrendered panels.
33536 createBody : function(){
33537 /** This region's body element
33538 * @type Roo.Element */
33539 this.bodyEl = this.el.createChild({
33541 cls: "roo-layout-panel-body tab-content" // bootstrap added...
33545 onRender: function(ctr, pos)
33547 var dh = Roo.DomHelper;
33548 /** This region's container element
33549 * @type Roo.Element */
33550 this.el = dh.append(ctr.dom, {
33552 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
33554 /** This region's title element
33555 * @type Roo.Element */
33557 this.titleEl = dh.append(this.el.dom,
33560 unselectable: "on",
33561 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
33563 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
33564 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
33567 this.titleEl.enableDisplayMode();
33568 /** This region's title text element
33569 * @type HTMLElement */
33570 this.titleTextEl = this.titleEl.dom.firstChild;
33571 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33573 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
33574 this.closeBtn.enableDisplayMode();
33575 this.closeBtn.on("click", this.closeClicked, this);
33576 this.closeBtn.hide();
33578 this.createBody(this.config);
33579 if(this.config.hideWhenEmpty){
33581 this.on("paneladded", this.validateVisibility, this);
33582 this.on("panelremoved", this.validateVisibility, this);
33584 if(this.autoScroll){
33585 this.bodyEl.setStyle("overflow", "auto");
33587 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
33589 //if(c.titlebar !== false){
33590 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
33591 this.titleEl.hide();
33593 this.titleEl.show();
33594 if(this.config.title){
33595 this.titleTextEl.innerHTML = this.config.title;
33599 if(this.config.collapsed){
33600 this.collapse(true);
33602 if(this.config.hidden){
33606 if (this.unrendered_panels && this.unrendered_panels.length) {
33607 for (var i =0;i< this.unrendered_panels.length; i++) {
33608 this.add(this.unrendered_panels[i]);
33610 this.unrendered_panels = null;
33616 applyConfig : function(c)
33619 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
33620 var dh = Roo.DomHelper;
33621 if(c.titlebar !== false){
33622 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
33623 this.collapseBtn.on("click", this.collapse, this);
33624 this.collapseBtn.enableDisplayMode();
33626 if(c.showPin === true || this.showPin){
33627 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
33628 this.stickBtn.enableDisplayMode();
33629 this.stickBtn.on("click", this.expand, this);
33630 this.stickBtn.hide();
33635 /** This region's collapsed element
33636 * @type Roo.Element */
33639 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33640 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33643 if(c.floatable !== false){
33644 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33645 this.collapsedEl.on("click", this.collapseClick, this);
33648 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33649 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33650 id: "message", unselectable: "on", style:{"float":"left"}});
33651 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33653 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33654 this.expandBtn.on("click", this.expand, this);
33658 if(this.collapseBtn){
33659 this.collapseBtn.setVisible(c.collapsible == true);
33662 this.cmargins = c.cmargins || this.cmargins ||
33663 (this.position == "west" || this.position == "east" ?
33664 {top: 0, left: 2, right:2, bottom: 0} :
33665 {top: 2, left: 0, right:0, bottom: 2});
33667 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33670 this.bottomTabs = c.tabPosition != "top";
33672 this.autoScroll = c.autoScroll || false;
33677 this.duration = c.duration || .30;
33678 this.slideDuration = c.slideDuration || .45;
33683 * Returns true if this region is currently visible.
33684 * @return {Boolean}
33686 isVisible : function(){
33687 return this.visible;
33691 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33692 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
33694 //setCollapsedTitle : function(title){
33695 // title = title || " ";
33696 // if(this.collapsedTitleTextEl){
33697 // this.collapsedTitleTextEl.innerHTML = title;
33701 getBox : function(){
33703 // if(!this.collapsed){
33704 b = this.el.getBox(false, true);
33706 // b = this.collapsedEl.getBox(false, true);
33711 getMargins : function(){
33712 return this.margins;
33713 //return this.collapsed ? this.cmargins : this.margins;
33716 highlight : function(){
33717 this.el.addClass("x-layout-panel-dragover");
33720 unhighlight : function(){
33721 this.el.removeClass("x-layout-panel-dragover");
33724 updateBox : function(box)
33726 if (!this.bodyEl) {
33727 return; // not rendered yet..
33731 if(!this.collapsed){
33732 this.el.dom.style.left = box.x + "px";
33733 this.el.dom.style.top = box.y + "px";
33734 this.updateBody(box.width, box.height);
33736 this.collapsedEl.dom.style.left = box.x + "px";
33737 this.collapsedEl.dom.style.top = box.y + "px";
33738 this.collapsedEl.setSize(box.width, box.height);
33741 this.tabs.autoSizeTabs();
33745 updateBody : function(w, h)
33748 this.el.setWidth(w);
33749 w -= this.el.getBorderWidth("rl");
33750 if(this.config.adjustments){
33751 w += this.config.adjustments[0];
33754 if(h !== null && h > 0){
33755 this.el.setHeight(h);
33756 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33757 h -= this.el.getBorderWidth("tb");
33758 if(this.config.adjustments){
33759 h += this.config.adjustments[1];
33761 this.bodyEl.setHeight(h);
33763 h = this.tabs.syncHeight(h);
33766 if(this.panelSize){
33767 w = w !== null ? w : this.panelSize.width;
33768 h = h !== null ? h : this.panelSize.height;
33770 if(this.activePanel){
33771 var el = this.activePanel.getEl();
33772 w = w !== null ? w : el.getWidth();
33773 h = h !== null ? h : el.getHeight();
33774 this.panelSize = {width: w, height: h};
33775 this.activePanel.setSize(w, h);
33777 if(Roo.isIE && this.tabs){
33778 this.tabs.el.repaint();
33783 * Returns the container element for this region.
33784 * @return {Roo.Element}
33786 getEl : function(){
33791 * Hides this region.
33794 //if(!this.collapsed){
33795 this.el.dom.style.left = "-2000px";
33798 // this.collapsedEl.dom.style.left = "-2000px";
33799 // this.collapsedEl.hide();
33801 this.visible = false;
33802 this.fireEvent("visibilitychange", this, false);
33806 * Shows this region if it was previously hidden.
33809 //if(!this.collapsed){
33812 // this.collapsedEl.show();
33814 this.visible = true;
33815 this.fireEvent("visibilitychange", this, true);
33818 closeClicked : function(){
33819 if(this.activePanel){
33820 this.remove(this.activePanel);
33824 collapseClick : function(e){
33826 e.stopPropagation();
33829 e.stopPropagation();
33835 * Collapses this region.
33836 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33839 collapse : function(skipAnim, skipCheck = false){
33840 if(this.collapsed) {
33844 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
33846 this.collapsed = true;
33848 this.split.el.hide();
33850 if(this.config.animate && skipAnim !== true){
33851 this.fireEvent("invalidated", this);
33852 this.animateCollapse();
33854 this.el.setLocation(-20000,-20000);
33856 this.collapsedEl.show();
33857 this.fireEvent("collapsed", this);
33858 this.fireEvent("invalidated", this);
33864 animateCollapse : function(){
33869 * Expands this region if it was previously collapsed.
33870 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33871 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33874 expand : function(e, skipAnim){
33876 e.stopPropagation();
33878 if(!this.collapsed || this.el.hasActiveFx()) {
33882 this.afterSlideIn();
33885 this.collapsed = false;
33886 if(this.config.animate && skipAnim !== true){
33887 this.animateExpand();
33891 this.split.el.show();
33893 this.collapsedEl.setLocation(-2000,-2000);
33894 this.collapsedEl.hide();
33895 this.fireEvent("invalidated", this);
33896 this.fireEvent("expanded", this);
33900 animateExpand : function(){
33904 initTabs : function()
33906 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
33908 var ts = new Roo.bootstrap.panel.Tabs({
33909 el: this.bodyEl.dom,
33910 tabPosition: this.bottomTabs ? 'bottom' : 'top',
33911 disableTooltips: this.config.disableTabTips,
33912 toolbar : this.config.toolbar
33915 if(this.config.hideTabs){
33916 ts.stripWrap.setDisplayed(false);
33919 ts.resizeTabs = this.config.resizeTabs === true;
33920 ts.minTabWidth = this.config.minTabWidth || 40;
33921 ts.maxTabWidth = this.config.maxTabWidth || 250;
33922 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33923 ts.monitorResize = false;
33924 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
33925 ts.bodyEl.addClass('roo-layout-tabs-body');
33926 this.panels.each(this.initPanelAsTab, this);
33929 initPanelAsTab : function(panel){
33930 var ti = this.tabs.addTab(
33934 this.config.closeOnTab && panel.isClosable(),
33937 if(panel.tabTip !== undefined){
33938 ti.setTooltip(panel.tabTip);
33940 ti.on("activate", function(){
33941 this.setActivePanel(panel);
33944 if(this.config.closeOnTab){
33945 ti.on("beforeclose", function(t, e){
33947 this.remove(panel);
33951 panel.tabItem = ti;
33956 updatePanelTitle : function(panel, title)
33958 if(this.activePanel == panel){
33959 this.updateTitle(title);
33962 var ti = this.tabs.getTab(panel.getEl().id);
33964 if(panel.tabTip !== undefined){
33965 ti.setTooltip(panel.tabTip);
33970 updateTitle : function(title){
33971 if(this.titleTextEl && !this.config.title){
33972 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
33976 setActivePanel : function(panel)
33978 panel = this.getPanel(panel);
33979 if(this.activePanel && this.activePanel != panel){
33980 this.activePanel.setActiveState(false);
33982 this.activePanel = panel;
33983 panel.setActiveState(true);
33984 if(this.panelSize){
33985 panel.setSize(this.panelSize.width, this.panelSize.height);
33988 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33990 this.updateTitle(panel.getTitle());
33992 this.fireEvent("invalidated", this);
33994 this.fireEvent("panelactivated", this, panel);
33998 * Shows the specified panel.
33999 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34000 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34002 showPanel : function(panel)
34004 panel = this.getPanel(panel);
34007 var tab = this.tabs.getTab(panel.getEl().id);
34008 if(tab.isHidden()){
34009 this.tabs.unhideTab(tab.id);
34013 this.setActivePanel(panel);
34020 * Get the active panel for this region.
34021 * @return {Roo.ContentPanel} The active panel or null
34023 getActivePanel : function(){
34024 return this.activePanel;
34027 validateVisibility : function(){
34028 if(this.panels.getCount() < 1){
34029 this.updateTitle(" ");
34030 this.closeBtn.hide();
34033 if(!this.isVisible()){
34040 * Adds the passed ContentPanel(s) to this region.
34041 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34042 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34044 add : function(panel)
34046 if(arguments.length > 1){
34047 for(var i = 0, len = arguments.length; i < len; i++) {
34048 this.add(arguments[i]);
34053 // if we have not been rendered yet, then we can not really do much of this..
34054 if (!this.bodyEl) {
34055 this.unrendered_panels.push(panel);
34062 if(this.hasPanel(panel)){
34063 this.showPanel(panel);
34066 panel.setRegion(this);
34067 this.panels.add(panel);
34068 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34069 // sinle panel - no tab...?? would it not be better to render it with the tabs,
34070 // and hide them... ???
34071 this.bodyEl.dom.appendChild(panel.getEl().dom);
34072 if(panel.background !== true){
34073 this.setActivePanel(panel);
34075 this.fireEvent("paneladded", this, panel);
34082 this.initPanelAsTab(panel);
34086 if(panel.background !== true){
34087 this.tabs.activate(panel.getEl().id);
34089 this.fireEvent("paneladded", this, panel);
34094 * Hides the tab for the specified panel.
34095 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34097 hidePanel : function(panel){
34098 if(this.tabs && (panel = this.getPanel(panel))){
34099 this.tabs.hideTab(panel.getEl().id);
34104 * Unhides the tab for a previously hidden panel.
34105 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34107 unhidePanel : function(panel){
34108 if(this.tabs && (panel = this.getPanel(panel))){
34109 this.tabs.unhideTab(panel.getEl().id);
34113 clearPanels : function(){
34114 while(this.panels.getCount() > 0){
34115 this.remove(this.panels.first());
34120 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34121 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34122 * @param {Boolean} preservePanel Overrides the config preservePanel option
34123 * @return {Roo.ContentPanel} The panel that was removed
34125 remove : function(panel, preservePanel)
34127 panel = this.getPanel(panel);
34132 this.fireEvent("beforeremove", this, panel, e);
34133 if(e.cancel === true){
34136 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34137 var panelId = panel.getId();
34138 this.panels.removeKey(panelId);
34140 document.body.appendChild(panel.getEl().dom);
34143 this.tabs.removeTab(panel.getEl().id);
34144 }else if (!preservePanel){
34145 this.bodyEl.dom.removeChild(panel.getEl().dom);
34147 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
34148 var p = this.panels.first();
34149 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
34150 tempEl.appendChild(p.getEl().dom);
34151 this.bodyEl.update("");
34152 this.bodyEl.dom.appendChild(p.getEl().dom);
34154 this.updateTitle(p.getTitle());
34156 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34157 this.setActivePanel(p);
34159 panel.setRegion(null);
34160 if(this.activePanel == panel){
34161 this.activePanel = null;
34163 if(this.config.autoDestroy !== false && preservePanel !== true){
34164 try{panel.destroy();}catch(e){}
34166 this.fireEvent("panelremoved", this, panel);
34171 * Returns the TabPanel component used by this region
34172 * @return {Roo.TabPanel}
34174 getTabs : function(){
34178 createTool : function(parentEl, className){
34179 var btn = Roo.DomHelper.append(parentEl, {
34181 cls: "x-layout-tools-button",
34184 cls: "roo-layout-tools-button-inner " + className,
34188 btn.addClassOnOver("roo-layout-tools-button-over");
34193 * Ext JS Library 1.1.1
34194 * Copyright(c) 2006-2007, Ext JS, LLC.
34196 * Originally Released Under LGPL - original licence link has changed is not relivant.
34199 * <script type="text/javascript">
34205 * @class Roo.SplitLayoutRegion
34206 * @extends Roo.LayoutRegion
34207 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34209 Roo.bootstrap.layout.Split = function(config){
34210 this.cursor = config.cursor;
34211 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
34214 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
34216 splitTip : "Drag to resize.",
34217 collapsibleSplitTip : "Drag to resize. Double click to hide.",
34218 useSplitTips : false,
34220 applyConfig : function(config){
34221 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
34224 onRender : function(ctr,pos) {
34226 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
34227 if(!this.config.split){
34232 var splitEl = Roo.DomHelper.append(ctr.dom, {
34234 id: this.el.id + "-split",
34235 cls: "roo-layout-split roo-layout-split-"+this.position,
34238 /** The SplitBar for this region
34239 * @type Roo.SplitBar */
34240 // does not exist yet...
34241 Roo.log([this.position, this.orientation]);
34243 this.split = new Roo.bootstrap.SplitBar({
34244 dragElement : splitEl,
34245 resizingElement: this.el,
34246 orientation : this.orientation
34249 this.split.on("moved", this.onSplitMove, this);
34250 this.split.useShim = this.config.useShim === true;
34251 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34252 if(this.useSplitTips){
34253 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34255 //if(config.collapsible){
34256 // this.split.el.on("dblclick", this.collapse, this);
34259 if(typeof this.config.minSize != "undefined"){
34260 this.split.minSize = this.config.minSize;
34262 if(typeof this.config.maxSize != "undefined"){
34263 this.split.maxSize = this.config.maxSize;
34265 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
34266 this.hideSplitter();
34271 getHMaxSize : function(){
34272 var cmax = this.config.maxSize || 10000;
34273 var center = this.mgr.getRegion("center");
34274 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34277 getVMaxSize : function(){
34278 var cmax = this.config.maxSize || 10000;
34279 var center = this.mgr.getRegion("center");
34280 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34283 onSplitMove : function(split, newSize){
34284 this.fireEvent("resized", this, newSize);
34288 * Returns the {@link Roo.SplitBar} for this region.
34289 * @return {Roo.SplitBar}
34291 getSplitBar : function(){
34296 this.hideSplitter();
34297 Roo.bootstrap.layout.Split.superclass.hide.call(this);
34300 hideSplitter : function(){
34302 this.split.el.setLocation(-2000,-2000);
34303 this.split.el.hide();
34309 this.split.el.show();
34311 Roo.bootstrap.layout.Split.superclass.show.call(this);
34314 beforeSlide: function(){
34315 if(Roo.isGecko){// firefox overflow auto bug workaround
34316 this.bodyEl.clip();
34318 this.tabs.bodyEl.clip();
34320 if(this.activePanel){
34321 this.activePanel.getEl().clip();
34323 if(this.activePanel.beforeSlide){
34324 this.activePanel.beforeSlide();
34330 afterSlide : function(){
34331 if(Roo.isGecko){// firefox overflow auto bug workaround
34332 this.bodyEl.unclip();
34334 this.tabs.bodyEl.unclip();
34336 if(this.activePanel){
34337 this.activePanel.getEl().unclip();
34338 if(this.activePanel.afterSlide){
34339 this.activePanel.afterSlide();
34345 initAutoHide : function(){
34346 if(this.autoHide !== false){
34347 if(!this.autoHideHd){
34348 var st = new Roo.util.DelayedTask(this.slideIn, this);
34349 this.autoHideHd = {
34350 "mouseout": function(e){
34351 if(!e.within(this.el, true)){
34355 "mouseover" : function(e){
34361 this.el.on(this.autoHideHd);
34365 clearAutoHide : function(){
34366 if(this.autoHide !== false){
34367 this.el.un("mouseout", this.autoHideHd.mouseout);
34368 this.el.un("mouseover", this.autoHideHd.mouseover);
34372 clearMonitor : function(){
34373 Roo.get(document).un("click", this.slideInIf, this);
34376 // these names are backwards but not changed for compat
34377 slideOut : function(){
34378 if(this.isSlid || this.el.hasActiveFx()){
34381 this.isSlid = true;
34382 if(this.collapseBtn){
34383 this.collapseBtn.hide();
34385 this.closeBtnState = this.closeBtn.getStyle('display');
34386 this.closeBtn.hide();
34388 this.stickBtn.show();
34391 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34392 this.beforeSlide();
34393 this.el.setStyle("z-index", 10001);
34394 this.el.slideIn(this.getSlideAnchor(), {
34395 callback: function(){
34397 this.initAutoHide();
34398 Roo.get(document).on("click", this.slideInIf, this);
34399 this.fireEvent("slideshow", this);
34406 afterSlideIn : function(){
34407 this.clearAutoHide();
34408 this.isSlid = false;
34409 this.clearMonitor();
34410 this.el.setStyle("z-index", "");
34411 if(this.collapseBtn){
34412 this.collapseBtn.show();
34414 this.closeBtn.setStyle('display', this.closeBtnState);
34416 this.stickBtn.hide();
34418 this.fireEvent("slidehide", this);
34421 slideIn : function(cb){
34422 if(!this.isSlid || this.el.hasActiveFx()){
34426 this.isSlid = false;
34427 this.beforeSlide();
34428 this.el.slideOut(this.getSlideAnchor(), {
34429 callback: function(){
34430 this.el.setLeftTop(-10000, -10000);
34432 this.afterSlideIn();
34440 slideInIf : function(e){
34441 if(!e.within(this.el)){
34446 animateCollapse : function(){
34447 this.beforeSlide();
34448 this.el.setStyle("z-index", 20000);
34449 var anchor = this.getSlideAnchor();
34450 this.el.slideOut(anchor, {
34451 callback : function(){
34452 this.el.setStyle("z-index", "");
34453 this.collapsedEl.slideIn(anchor, {duration:.3});
34455 this.el.setLocation(-10000,-10000);
34457 this.fireEvent("collapsed", this);
34464 animateExpand : function(){
34465 this.beforeSlide();
34466 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34467 this.el.setStyle("z-index", 20000);
34468 this.collapsedEl.hide({
34471 this.el.slideIn(this.getSlideAnchor(), {
34472 callback : function(){
34473 this.el.setStyle("z-index", "");
34476 this.split.el.show();
34478 this.fireEvent("invalidated", this);
34479 this.fireEvent("expanded", this);
34507 getAnchor : function(){
34508 return this.anchors[this.position];
34511 getCollapseAnchor : function(){
34512 return this.canchors[this.position];
34515 getSlideAnchor : function(){
34516 return this.sanchors[this.position];
34519 getAlignAdj : function(){
34520 var cm = this.cmargins;
34521 switch(this.position){
34537 getExpandAdj : function(){
34538 var c = this.collapsedEl, cm = this.cmargins;
34539 switch(this.position){
34541 return [-(cm.right+c.getWidth()+cm.left), 0];
34544 return [cm.right+c.getWidth()+cm.left, 0];
34547 return [0, -(cm.top+cm.bottom+c.getHeight())];
34550 return [0, cm.top+cm.bottom+c.getHeight()];
34556 * Ext JS Library 1.1.1
34557 * Copyright(c) 2006-2007, Ext JS, LLC.
34559 * Originally Released Under LGPL - original licence link has changed is not relivant.
34562 * <script type="text/javascript">
34565 * These classes are private internal classes
34567 Roo.bootstrap.layout.Center = function(config){
34568 config.region = "center";
34569 Roo.bootstrap.layout.Region.call(this, config);
34570 this.visible = true;
34571 this.minWidth = config.minWidth || 20;
34572 this.minHeight = config.minHeight || 20;
34575 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
34577 // center panel can't be hidden
34581 // center panel can't be hidden
34584 getMinWidth: function(){
34585 return this.minWidth;
34588 getMinHeight: function(){
34589 return this.minHeight;
34602 Roo.bootstrap.layout.North = function(config)
34604 config.region = 'north';
34605 config.cursor = 'n-resize';
34607 Roo.bootstrap.layout.Split.call(this, config);
34611 this.split.placement = Roo.bootstrap.SplitBar.TOP;
34612 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34613 this.split.el.addClass("roo-layout-split-v");
34615 var size = config.initialSize || config.height;
34616 if(typeof size != "undefined"){
34617 this.el.setHeight(size);
34620 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
34622 orientation: Roo.bootstrap.SplitBar.VERTICAL,
34626 getBox : function(){
34627 if(this.collapsed){
34628 return this.collapsedEl.getBox();
34630 var box = this.el.getBox();
34632 box.height += this.split.el.getHeight();
34637 updateBox : function(box){
34638 if(this.split && !this.collapsed){
34639 box.height -= this.split.el.getHeight();
34640 this.split.el.setLeft(box.x);
34641 this.split.el.setTop(box.y+box.height);
34642 this.split.el.setWidth(box.width);
34644 if(this.collapsed){
34645 this.updateBody(box.width, null);
34647 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34655 Roo.bootstrap.layout.South = function(config){
34656 config.region = 'south';
34657 config.cursor = 's-resize';
34658 Roo.bootstrap.layout.Split.call(this, config);
34660 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
34661 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34662 this.split.el.addClass("roo-layout-split-v");
34664 var size = config.initialSize || config.height;
34665 if(typeof size != "undefined"){
34666 this.el.setHeight(size);
34670 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
34671 orientation: Roo.bootstrap.SplitBar.VERTICAL,
34672 getBox : function(){
34673 if(this.collapsed){
34674 return this.collapsedEl.getBox();
34676 var box = this.el.getBox();
34678 var sh = this.split.el.getHeight();
34685 updateBox : function(box){
34686 if(this.split && !this.collapsed){
34687 var sh = this.split.el.getHeight();
34690 this.split.el.setLeft(box.x);
34691 this.split.el.setTop(box.y-sh);
34692 this.split.el.setWidth(box.width);
34694 if(this.collapsed){
34695 this.updateBody(box.width, null);
34697 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34701 Roo.bootstrap.layout.East = function(config){
34702 config.region = "east";
34703 config.cursor = "e-resize";
34704 Roo.bootstrap.layout.Split.call(this, config);
34706 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
34707 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34708 this.split.el.addClass("roo-layout-split-h");
34710 var size = config.initialSize || config.width;
34711 if(typeof size != "undefined"){
34712 this.el.setWidth(size);
34715 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
34716 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34717 getBox : function(){
34718 if(this.collapsed){
34719 return this.collapsedEl.getBox();
34721 var box = this.el.getBox();
34723 var sw = this.split.el.getWidth();
34730 updateBox : function(box){
34731 if(this.split && !this.collapsed){
34732 var sw = this.split.el.getWidth();
34734 this.split.el.setLeft(box.x);
34735 this.split.el.setTop(box.y);
34736 this.split.el.setHeight(box.height);
34739 if(this.collapsed){
34740 this.updateBody(null, box.height);
34742 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34746 Roo.bootstrap.layout.West = function(config){
34747 config.region = "west";
34748 config.cursor = "w-resize";
34750 Roo.bootstrap.layout.Split.call(this, config);
34752 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
34753 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34754 this.split.el.addClass("roo-layout-split-h");
34758 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
34759 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34761 onRender: function(ctr, pos)
34763 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
34764 var size = this.config.initialSize || this.config.width;
34765 if(typeof size != "undefined"){
34766 this.el.setWidth(size);
34770 getBox : function(){
34771 if(this.collapsed){
34772 return this.collapsedEl.getBox();
34774 var box = this.el.getBox();
34776 box.width += this.split.el.getWidth();
34781 updateBox : function(box){
34782 if(this.split && !this.collapsed){
34783 var sw = this.split.el.getWidth();
34785 this.split.el.setLeft(box.x+box.width);
34786 this.split.el.setTop(box.y);
34787 this.split.el.setHeight(box.height);
34789 if(this.collapsed){
34790 this.updateBody(null, box.height);
34792 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34795 Roo.namespace("Roo.bootstrap.panel");/*
34797 * Ext JS Library 1.1.1
34798 * Copyright(c) 2006-2007, Ext JS, LLC.
34800 * Originally Released Under LGPL - original licence link has changed is not relivant.
34803 * <script type="text/javascript">
34806 * @class Roo.ContentPanel
34807 * @extends Roo.util.Observable
34808 * A basic ContentPanel element.
34809 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
34810 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
34811 * @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
34812 * @cfg {Boolean} closable True if the panel can be closed/removed
34813 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
34814 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34815 * @cfg {Toolbar} toolbar A toolbar for this panel
34816 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
34817 * @cfg {String} title The title for this panel
34818 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34819 * @cfg {String} url Calls {@link #setUrl} with this value
34820 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34821 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
34822 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
34823 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
34824 * @cfg {Boolean} badges render the badges
34827 * Create a new ContentPanel.
34828 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34829 * @param {String/Object} config A string to set only the title or a config object
34830 * @param {String} content (optional) Set the HTML content for this panel
34831 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34833 Roo.bootstrap.panel.Content = function( config){
34835 this.tpl = config.tpl || false;
34837 var el = config.el;
34838 var content = config.content;
34840 if(config.autoCreate){ // xtype is available if this is called from factory
34843 this.el = Roo.get(el);
34844 if(!this.el && config && config.autoCreate){
34845 if(typeof config.autoCreate == "object"){
34846 if(!config.autoCreate.id){
34847 config.autoCreate.id = config.id||el;
34849 this.el = Roo.DomHelper.append(document.body,
34850 config.autoCreate, true);
34852 var elcfg = { tag: "div",
34853 cls: "roo-layout-inactive-content",
34857 elcfg.html = config.html;
34861 this.el = Roo.DomHelper.append(document.body, elcfg , true);
34864 this.closable = false;
34865 this.loaded = false;
34866 this.active = false;
34869 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
34871 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
34873 this.wrapEl = this.el; //this.el.wrap();
34875 if (config.toolbar.items) {
34876 ti = config.toolbar.items ;
34877 delete config.toolbar.items ;
34881 this.toolbar.render(this.wrapEl, 'before');
34882 for(var i =0;i < ti.length;i++) {
34883 // Roo.log(['add child', items[i]]);
34884 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34886 this.toolbar.items = nitems;
34887 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
34888 delete config.toolbar;
34892 // xtype created footer. - not sure if will work as we normally have to render first..
34893 if (this.footer && !this.footer.el && this.footer.xtype) {
34894 if (!this.wrapEl) {
34895 this.wrapEl = this.el.wrap();
34898 this.footer.container = this.wrapEl.createChild();
34900 this.footer = Roo.factory(this.footer, Roo);
34905 if(typeof config == "string"){
34906 this.title = config;
34908 Roo.apply(this, config);
34912 this.resizeEl = Roo.get(this.resizeEl, true);
34914 this.resizeEl = this.el;
34916 // handle view.xtype
34924 * Fires when this panel is activated.
34925 * @param {Roo.ContentPanel} this
34929 * @event deactivate
34930 * Fires when this panel is activated.
34931 * @param {Roo.ContentPanel} this
34933 "deactivate" : true,
34937 * Fires when this panel is resized if fitToFrame is true.
34938 * @param {Roo.ContentPanel} this
34939 * @param {Number} width The width after any component adjustments
34940 * @param {Number} height The height after any component adjustments
34946 * Fires when this tab is created
34947 * @param {Roo.ContentPanel} this
34958 if(this.autoScroll){
34959 this.resizeEl.setStyle("overflow", "auto");
34961 // fix randome scrolling
34962 //this.el.on('scroll', function() {
34963 // Roo.log('fix random scolling');
34964 // this.scrollTo('top',0);
34967 content = content || this.content;
34969 this.setContent(content);
34971 if(config && config.url){
34972 this.setUrl(this.url, this.params, this.loadOnce);
34977 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
34979 if (this.view && typeof(this.view.xtype) != 'undefined') {
34980 this.view.el = this.el.appendChild(document.createElement("div"));
34981 this.view = Roo.factory(this.view);
34982 this.view.render && this.view.render(false, '');
34986 this.fireEvent('render', this);
34989 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
34993 setRegion : function(region){
34994 this.region = region;
34995 this.setActiveClass(region && !this.background);
34999 setActiveClass: function(state)
35002 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
35003 this.el.setStyle('position','relative');
35005 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
35006 this.el.setStyle('position', 'absolute');
35011 * Returns the toolbar for this Panel if one was configured.
35012 * @return {Roo.Toolbar}
35014 getToolbar : function(){
35015 return this.toolbar;
35018 setActiveState : function(active)
35020 this.active = active;
35021 this.setActiveClass(active);
35023 this.fireEvent("deactivate", this);
35025 this.fireEvent("activate", this);
35029 * Updates this panel's element
35030 * @param {String} content The new content
35031 * @param {Boolean} loadScripts (optional) true to look for and process scripts
35033 setContent : function(content, loadScripts){
35034 this.el.update(content, loadScripts);
35037 ignoreResize : function(w, h){
35038 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35041 this.lastSize = {width: w, height: h};
35046 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35047 * @return {Roo.UpdateManager} The UpdateManager
35049 getUpdateManager : function(){
35050 return this.el.getUpdateManager();
35053 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35054 * @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:
35057 url: "your-url.php",
35058 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35059 callback: yourFunction,
35060 scope: yourObject, //(optional scope)
35063 text: "Loading...",
35068 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35069 * 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.
35070 * @param {String/Object} params (optional) The parameters to pass as either a URL encoded string "param1=1&param2=2" or an object {param1: 1, param2: 2}
35071 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35072 * @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.
35073 * @return {Roo.ContentPanel} this
35076 var um = this.el.getUpdateManager();
35077 um.update.apply(um, arguments);
35083 * 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.
35084 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35085 * @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)
35086 * @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)
35087 * @return {Roo.UpdateManager} The UpdateManager
35089 setUrl : function(url, params, loadOnce){
35090 if(this.refreshDelegate){
35091 this.removeListener("activate", this.refreshDelegate);
35093 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35094 this.on("activate", this.refreshDelegate);
35095 return this.el.getUpdateManager();
35098 _handleRefresh : function(url, params, loadOnce){
35099 if(!loadOnce || !this.loaded){
35100 var updater = this.el.getUpdateManager();
35101 updater.update(url, params, this._setLoaded.createDelegate(this));
35105 _setLoaded : function(){
35106 this.loaded = true;
35110 * Returns this panel's id
35113 getId : function(){
35118 * Returns this panel's element - used by regiosn to add.
35119 * @return {Roo.Element}
35121 getEl : function(){
35122 return this.wrapEl || this.el;
35127 adjustForComponents : function(width, height)
35129 //Roo.log('adjustForComponents ');
35130 if(this.resizeEl != this.el){
35131 width -= this.el.getFrameWidth('lr');
35132 height -= this.el.getFrameWidth('tb');
35135 var te = this.toolbar.getEl();
35136 height -= te.getHeight();
35137 te.setWidth(width);
35140 var te = this.footer.getEl();
35141 Roo.log("footer:" + te.getHeight());
35143 height -= te.getHeight();
35144 te.setWidth(width);
35148 if(this.adjustments){
35149 width += this.adjustments[0];
35150 height += this.adjustments[1];
35152 return {"width": width, "height": height};
35155 setSize : function(width, height){
35156 if(this.fitToFrame && !this.ignoreResize(width, height)){
35157 if(this.fitContainer && this.resizeEl != this.el){
35158 this.el.setSize(width, height);
35160 var size = this.adjustForComponents(width, height);
35161 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
35162 this.fireEvent('resize', this, size.width, size.height);
35167 * Returns this panel's title
35170 getTitle : function(){
35175 * Set this panel's title
35176 * @param {String} title
35178 setTitle : function(title){
35179 this.title = title;
35181 this.region.updatePanelTitle(this, title);
35186 * Returns true is this panel was configured to be closable
35187 * @return {Boolean}
35189 isClosable : function(){
35190 return this.closable;
35193 beforeSlide : function(){
35195 this.resizeEl.clip();
35198 afterSlide : function(){
35200 this.resizeEl.unclip();
35204 * Force a content refresh from the URL specified in the {@link #setUrl} method.
35205 * Will fail silently if the {@link #setUrl} method has not been called.
35206 * This does not activate the panel, just updates its content.
35208 refresh : function(){
35209 if(this.refreshDelegate){
35210 this.loaded = false;
35211 this.refreshDelegate();
35216 * Destroys this panel
35218 destroy : function(){
35219 this.el.removeAllListeners();
35220 var tempEl = document.createElement("span");
35221 tempEl.appendChild(this.el.dom);
35222 tempEl.innerHTML = "";
35228 * form - if the content panel contains a form - this is a reference to it.
35229 * @type {Roo.form.Form}
35233 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35234 * This contains a reference to it.
35240 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35250 * @param {Object} cfg Xtype definition of item to add.
35254 getChildContainer: function () {
35255 return this.getEl();
35260 var ret = new Roo.factory(cfg);
35265 if (cfg.xtype.match(/^Form$/)) {
35268 //if (this.footer) {
35269 // el = this.footer.container.insertSibling(false, 'before');
35271 el = this.el.createChild();
35274 this.form = new Roo.form.Form(cfg);
35277 if ( this.form.allItems.length) {
35278 this.form.render(el.dom);
35282 // should only have one of theses..
35283 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35284 // views.. should not be just added - used named prop 'view''
35286 cfg.el = this.el.appendChild(document.createElement("div"));
35289 var ret = new Roo.factory(cfg);
35291 ret.render && ret.render(false, ''); // render blank..
35301 * @class Roo.bootstrap.panel.Grid
35302 * @extends Roo.bootstrap.panel.Content
35304 * Create a new GridPanel.
35305 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
35306 * @param {Object} config A the config object
35312 Roo.bootstrap.panel.Grid = function(config)
35316 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35317 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
35319 config.el = this.wrapper;
35320 //this.el = this.wrapper;
35322 if (config.container) {
35323 // ctor'ed from a Border/panel.grid
35326 this.wrapper.setStyle("overflow", "hidden");
35327 this.wrapper.addClass('roo-grid-container');
35332 if(config.toolbar){
35333 var tool_el = this.wrapper.createChild();
35334 this.toolbar = Roo.factory(config.toolbar);
35336 if (config.toolbar.items) {
35337 ti = config.toolbar.items ;
35338 delete config.toolbar.items ;
35342 this.toolbar.render(tool_el);
35343 for(var i =0;i < ti.length;i++) {
35344 // Roo.log(['add child', items[i]]);
35345 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
35347 this.toolbar.items = nitems;
35349 delete config.toolbar;
35352 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
35353 config.grid.scrollBody = true;;
35354 config.grid.monitorWindowResize = false; // turn off autosizing
35355 config.grid.autoHeight = false;
35356 config.grid.autoWidth = false;
35358 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
35360 if (config.background) {
35361 // render grid on panel activation (if panel background)
35362 this.on('activate', function(gp) {
35363 if (!gp.grid.rendered) {
35364 gp.grid.render(this.wrapper);
35365 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
35370 this.grid.render(this.wrapper);
35371 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
35374 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
35375 // ??? needed ??? config.el = this.wrapper;
35380 // xtype created footer. - not sure if will work as we normally have to render first..
35381 if (this.footer && !this.footer.el && this.footer.xtype) {
35383 var ctr = this.grid.getView().getFooterPanel(true);
35384 this.footer.dataSource = this.grid.dataSource;
35385 this.footer = Roo.factory(this.footer, Roo);
35386 this.footer.render(ctr);
35396 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
35397 getId : function(){
35398 return this.grid.id;
35402 * Returns the grid for this panel
35403 * @return {Roo.bootstrap.Table}
35405 getGrid : function(){
35409 setSize : function(width, height){
35410 if(!this.ignoreResize(width, height)){
35411 var grid = this.grid;
35412 var size = this.adjustForComponents(width, height);
35413 var gridel = grid.getGridEl();
35414 gridel.setSize(size.width, size.height);
35416 var thd = grid.getGridEl().select('thead',true).first();
35417 var tbd = grid.getGridEl().select('tbody', true).first();
35419 tbd.setSize(width, height - thd.getHeight());
35428 beforeSlide : function(){
35429 this.grid.getView().scroller.clip();
35432 afterSlide : function(){
35433 this.grid.getView().scroller.unclip();
35436 destroy : function(){
35437 this.grid.destroy();
35439 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
35444 * @class Roo.bootstrap.panel.Nest
35445 * @extends Roo.bootstrap.panel.Content
35447 * Create a new Panel, that can contain a layout.Border.
35450 * @param {Roo.BorderLayout} layout The layout for this panel
35451 * @param {String/Object} config A string to set only the title or a config object
35453 Roo.bootstrap.panel.Nest = function(config)
35455 // construct with only one argument..
35456 /* FIXME - implement nicer consturctors
35457 if (layout.layout) {
35459 layout = config.layout;
35460 delete config.layout;
35462 if (layout.xtype && !layout.getEl) {
35463 // then layout needs constructing..
35464 layout = Roo.factory(layout, Roo);
35468 config.el = config.layout.getEl();
35470 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
35472 config.layout.monitorWindowResize = false; // turn off autosizing
35473 this.layout = config.layout;
35474 this.layout.getEl().addClass("roo-layout-nested-layout");
35481 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
35483 setSize : function(width, height){
35484 if(!this.ignoreResize(width, height)){
35485 var size = this.adjustForComponents(width, height);
35486 var el = this.layout.getEl();
35487 if (size.height < 1) {
35488 el.setWidth(size.width);
35490 el.setSize(size.width, size.height);
35492 var touch = el.dom.offsetWidth;
35493 this.layout.layout();
35494 // ie requires a double layout on the first pass
35495 if(Roo.isIE && !this.initialized){
35496 this.initialized = true;
35497 this.layout.layout();
35502 // activate all subpanels if not currently active..
35504 setActiveState : function(active){
35505 this.active = active;
35506 this.setActiveClass(active);
35509 this.fireEvent("deactivate", this);
35513 this.fireEvent("activate", this);
35514 // not sure if this should happen before or after..
35515 if (!this.layout) {
35516 return; // should not happen..
35519 for (var r in this.layout.regions) {
35520 reg = this.layout.getRegion(r);
35521 if (reg.getActivePanel()) {
35522 //reg.showPanel(reg.getActivePanel()); // force it to activate..
35523 reg.setActivePanel(reg.getActivePanel());
35526 if (!reg.panels.length) {
35529 reg.showPanel(reg.getPanel(0));
35538 * Returns the nested BorderLayout for this panel
35539 * @return {Roo.BorderLayout}
35541 getLayout : function(){
35542 return this.layout;
35546 * Adds a xtype elements to the layout of the nested panel
35550 xtype : 'ContentPanel',
35557 xtype : 'NestedLayoutPanel',
35563 items : [ ... list of content panels or nested layout panels.. ]
35567 * @param {Object} cfg Xtype definition of item to add.
35569 addxtype : function(cfg) {
35570 return this.layout.addxtype(cfg);
35575 * Ext JS Library 1.1.1
35576 * Copyright(c) 2006-2007, Ext JS, LLC.
35578 * Originally Released Under LGPL - original licence link has changed is not relivant.
35581 * <script type="text/javascript">
35584 * @class Roo.TabPanel
35585 * @extends Roo.util.Observable
35586 * A lightweight tab container.
35590 // basic tabs 1, built from existing content
35591 var tabs = new Roo.TabPanel("tabs1");
35592 tabs.addTab("script", "View Script");
35593 tabs.addTab("markup", "View Markup");
35594 tabs.activate("script");
35596 // more advanced tabs, built from javascript
35597 var jtabs = new Roo.TabPanel("jtabs");
35598 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
35600 // set up the UpdateManager
35601 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
35602 var updater = tab2.getUpdateManager();
35603 updater.setDefaultUrl("ajax1.htm");
35604 tab2.on('activate', updater.refresh, updater, true);
35606 // Use setUrl for Ajax loading
35607 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
35608 tab3.setUrl("ajax2.htm", null, true);
35611 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
35614 jtabs.activate("jtabs-1");
35617 * Create a new TabPanel.
35618 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
35619 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
35621 Roo.bootstrap.panel.Tabs = function(config){
35623 * The container element for this TabPanel.
35624 * @type Roo.Element
35626 this.el = Roo.get(config.el);
35629 if(typeof config == "boolean"){
35630 this.tabPosition = config ? "bottom" : "top";
35632 Roo.apply(this, config);
35636 if(this.tabPosition == "bottom"){
35637 this.bodyEl = Roo.get(this.createBody(this.el.dom));
35638 this.el.addClass("roo-tabs-bottom");
35640 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
35641 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
35642 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
35644 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
35646 if(this.tabPosition != "bottom"){
35647 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
35648 * @type Roo.Element
35650 this.bodyEl = Roo.get(this.createBody(this.el.dom));
35651 this.el.addClass("roo-tabs-top");
35655 this.bodyEl.setStyle("position", "relative");
35657 this.active = null;
35658 this.activateDelegate = this.activate.createDelegate(this);
35663 * Fires when the active tab changes
35664 * @param {Roo.TabPanel} this
35665 * @param {Roo.TabPanelItem} activePanel The new active tab
35669 * @event beforetabchange
35670 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
35671 * @param {Roo.TabPanel} this
35672 * @param {Object} e Set cancel to true on this object to cancel the tab change
35673 * @param {Roo.TabPanelItem} tab The tab being changed to
35675 "beforetabchange" : true
35678 Roo.EventManager.onWindowResize(this.onResize, this);
35679 this.cpad = this.el.getPadding("lr");
35680 this.hiddenCount = 0;
35683 // toolbar on the tabbar support...
35684 if (this.toolbar) {
35685 alert("no toolbar support yet");
35686 this.toolbar = false;
35688 var tcfg = this.toolbar;
35689 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
35690 this.toolbar = new Roo.Toolbar(tcfg);
35691 if (Roo.isSafari) {
35692 var tbl = tcfg.container.child('table', true);
35693 tbl.setAttribute('width', '100%');
35701 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
35704 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
35706 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
35708 tabPosition : "top",
35710 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
35712 currentTabWidth : 0,
35714 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
35718 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
35722 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
35724 preferredTabWidth : 175,
35726 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
35728 resizeTabs : false,
35730 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
35732 monitorResize : true,
35734 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
35739 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
35740 * @param {String} id The id of the div to use <b>or create</b>
35741 * @param {String} text The text for the tab
35742 * @param {String} content (optional) Content to put in the TabPanelItem body
35743 * @param {Boolean} closable (optional) True to create a close icon on the tab
35744 * @return {Roo.TabPanelItem} The created TabPanelItem
35746 addTab : function(id, text, content, closable, tpl)
35748 var item = new Roo.bootstrap.panel.TabItem({
35752 closable : closable,
35755 this.addTabItem(item);
35757 item.setContent(content);
35763 * Returns the {@link Roo.TabPanelItem} with the specified id/index
35764 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
35765 * @return {Roo.TabPanelItem}
35767 getTab : function(id){
35768 return this.items[id];
35772 * Hides the {@link Roo.TabPanelItem} with the specified id/index
35773 * @param {String/Number} id The id or index of the TabPanelItem to hide.
35775 hideTab : function(id){
35776 var t = this.items[id];
35779 this.hiddenCount++;
35780 this.autoSizeTabs();
35785 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
35786 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
35788 unhideTab : function(id){
35789 var t = this.items[id];
35791 t.setHidden(false);
35792 this.hiddenCount--;
35793 this.autoSizeTabs();
35798 * Adds an existing {@link Roo.TabPanelItem}.
35799 * @param {Roo.TabPanelItem} item The TabPanelItem to add
35801 addTabItem : function(item){
35802 this.items[item.id] = item;
35803 this.items.push(item);
35804 // if(this.resizeTabs){
35805 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
35806 // this.autoSizeTabs();
35808 // item.autoSize();
35813 * Removes a {@link Roo.TabPanelItem}.
35814 * @param {String/Number} id The id or index of the TabPanelItem to remove.
35816 removeTab : function(id){
35817 var items = this.items;
35818 var tab = items[id];
35819 if(!tab) { return; }
35820 var index = items.indexOf(tab);
35821 if(this.active == tab && items.length > 1){
35822 var newTab = this.getNextAvailable(index);
35827 this.stripEl.dom.removeChild(tab.pnode.dom);
35828 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
35829 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
35831 items.splice(index, 1);
35832 delete this.items[tab.id];
35833 tab.fireEvent("close", tab);
35834 tab.purgeListeners();
35835 this.autoSizeTabs();
35838 getNextAvailable : function(start){
35839 var items = this.items;
35841 // look for a next tab that will slide over to
35842 // replace the one being removed
35843 while(index < items.length){
35844 var item = items[++index];
35845 if(item && !item.isHidden()){
35849 // if one isn't found select the previous tab (on the left)
35852 var item = items[--index];
35853 if(item && !item.isHidden()){
35861 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
35862 * @param {String/Number} id The id or index of the TabPanelItem to disable.
35864 disableTab : function(id){
35865 var tab = this.items[id];
35866 if(tab && this.active != tab){
35872 * Enables a {@link Roo.TabPanelItem} that is disabled.
35873 * @param {String/Number} id The id or index of the TabPanelItem to enable.
35875 enableTab : function(id){
35876 var tab = this.items[id];
35881 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
35882 * @param {String/Number} id The id or index of the TabPanelItem to activate.
35883 * @return {Roo.TabPanelItem} The TabPanelItem.
35885 activate : function(id){
35886 var tab = this.items[id];
35890 if(tab == this.active || tab.disabled){
35894 this.fireEvent("beforetabchange", this, e, tab);
35895 if(e.cancel !== true && !tab.disabled){
35897 this.active.hide();
35899 this.active = this.items[id];
35900 this.active.show();
35901 this.fireEvent("tabchange", this, this.active);
35907 * Gets the active {@link Roo.TabPanelItem}.
35908 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
35910 getActiveTab : function(){
35911 return this.active;
35915 * Updates the tab body element to fit the height of the container element
35916 * for overflow scrolling
35917 * @param {Number} targetHeight (optional) Override the starting height from the elements height
35919 syncHeight : function(targetHeight){
35920 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35921 var bm = this.bodyEl.getMargins();
35922 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
35923 this.bodyEl.setHeight(newHeight);
35927 onResize : function(){
35928 if(this.monitorResize){
35929 this.autoSizeTabs();
35934 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
35936 beginUpdate : function(){
35937 this.updating = true;
35941 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
35943 endUpdate : function(){
35944 this.updating = false;
35945 this.autoSizeTabs();
35949 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
35951 autoSizeTabs : function(){
35952 var count = this.items.length;
35953 var vcount = count - this.hiddenCount;
35954 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
35957 var w = Math.max(this.el.getWidth() - this.cpad, 10);
35958 var availWidth = Math.floor(w / vcount);
35959 var b = this.stripBody;
35960 if(b.getWidth() > w){
35961 var tabs = this.items;
35962 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
35963 if(availWidth < this.minTabWidth){
35964 /*if(!this.sleft){ // incomplete scrolling code
35965 this.createScrollButtons();
35968 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
35971 if(this.currentTabWidth < this.preferredTabWidth){
35972 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
35978 * Returns the number of tabs in this TabPanel.
35981 getCount : function(){
35982 return this.items.length;
35986 * Resizes all the tabs to the passed width
35987 * @param {Number} The new width
35989 setTabWidth : function(width){
35990 this.currentTabWidth = width;
35991 for(var i = 0, len = this.items.length; i < len; i++) {
35992 if(!this.items[i].isHidden()) {
35993 this.items[i].setWidth(width);
35999 * Destroys this TabPanel
36000 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
36002 destroy : function(removeEl){
36003 Roo.EventManager.removeResizeListener(this.onResize, this);
36004 for(var i = 0, len = this.items.length; i < len; i++){
36005 this.items[i].purgeListeners();
36007 if(removeEl === true){
36008 this.el.update("");
36013 createStrip : function(container)
36015 var strip = document.createElement("nav");
36016 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
36017 container.appendChild(strip);
36021 createStripList : function(strip)
36023 // div wrapper for retard IE
36024 // returns the "tr" element.
36025 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
36026 //'<div class="x-tabs-strip-wrap">'+
36027 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
36028 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
36029 return strip.firstChild; //.firstChild.firstChild.firstChild;
36031 createBody : function(container)
36033 var body = document.createElement("div");
36034 Roo.id(body, "tab-body");
36035 //Roo.fly(body).addClass("x-tabs-body");
36036 Roo.fly(body).addClass("tab-content");
36037 container.appendChild(body);
36040 createItemBody :function(bodyEl, id){
36041 var body = Roo.getDom(id);
36043 body = document.createElement("div");
36046 //Roo.fly(body).addClass("x-tabs-item-body");
36047 Roo.fly(body).addClass("tab-pane");
36048 bodyEl.insertBefore(body, bodyEl.firstChild);
36052 createStripElements : function(stripEl, text, closable, tpl)
36054 var td = document.createElement("li"); // was td..
36057 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
36060 stripEl.appendChild(td);
36062 td.className = "x-tabs-closable";
36063 if(!this.closeTpl){
36064 this.closeTpl = new Roo.Template(
36065 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36066 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
36067 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
36070 var el = this.closeTpl.overwrite(td, {"text": text});
36071 var close = el.getElementsByTagName("div")[0];
36072 var inner = el.getElementsByTagName("em")[0];
36073 return {"el": el, "close": close, "inner": inner};
36076 // not sure what this is..
36077 // if(!this.tabTpl){
36078 //this.tabTpl = new Roo.Template(
36079 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36080 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
36082 // this.tabTpl = new Roo.Template(
36083 // '<a href="#">' +
36084 // '<span unselectable="on"' +
36085 // (this.disableTooltips ? '' : ' title="{text}"') +
36086 // ' >{text}</span></a>'
36092 var template = tpl || this.tabTpl || false;
36096 template = new Roo.Template(
36098 '<span unselectable="on"' +
36099 (this.disableTooltips ? '' : ' title="{text}"') +
36100 ' >{text}</span></a>'
36104 switch (typeof(template)) {
36108 template = new Roo.Template(template);
36114 var el = template.overwrite(td, {"text": text});
36116 var inner = el.getElementsByTagName("span")[0];
36118 return {"el": el, "inner": inner};
36126 * @class Roo.TabPanelItem
36127 * @extends Roo.util.Observable
36128 * Represents an individual item (tab plus body) in a TabPanel.
36129 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
36130 * @param {String} id The id of this TabPanelItem
36131 * @param {String} text The text for the tab of this TabPanelItem
36132 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
36134 Roo.bootstrap.panel.TabItem = function(config){
36136 * The {@link Roo.TabPanel} this TabPanelItem belongs to
36137 * @type Roo.TabPanel
36139 this.tabPanel = config.panel;
36141 * The id for this TabPanelItem
36144 this.id = config.id;
36146 this.disabled = false;
36148 this.text = config.text;
36150 this.loaded = false;
36151 this.closable = config.closable;
36154 * The body element for this TabPanelItem.
36155 * @type Roo.Element
36157 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
36158 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
36159 this.bodyEl.setStyle("display", "block");
36160 this.bodyEl.setStyle("zoom", "1");
36161 //this.hideAction();
36163 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
36165 this.el = Roo.get(els.el);
36166 this.inner = Roo.get(els.inner, true);
36167 this.textEl = Roo.get(this.el.dom.firstChild, true);
36168 this.pnode = Roo.get(els.el.parentNode, true);
36169 this.el.on("mousedown", this.onTabMouseDown, this);
36170 this.el.on("click", this.onTabClick, this);
36172 if(config.closable){
36173 var c = Roo.get(els.close, true);
36174 c.dom.title = this.closeText;
36175 c.addClassOnOver("close-over");
36176 c.on("click", this.closeClick, this);
36182 * Fires when this tab becomes the active tab.
36183 * @param {Roo.TabPanel} tabPanel The parent TabPanel
36184 * @param {Roo.TabPanelItem} this
36188 * @event beforeclose
36189 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
36190 * @param {Roo.TabPanelItem} this
36191 * @param {Object} e Set cancel to true on this object to cancel the close.
36193 "beforeclose": true,
36196 * Fires when this tab is closed.
36197 * @param {Roo.TabPanelItem} this
36201 * @event deactivate
36202 * Fires when this tab is no longer the active tab.
36203 * @param {Roo.TabPanel} tabPanel The parent TabPanel
36204 * @param {Roo.TabPanelItem} this
36206 "deactivate" : true
36208 this.hidden = false;
36210 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
36213 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
36215 purgeListeners : function(){
36216 Roo.util.Observable.prototype.purgeListeners.call(this);
36217 this.el.removeAllListeners();
36220 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
36223 this.pnode.addClass("active");
36226 this.tabPanel.stripWrap.repaint();
36228 this.fireEvent("activate", this.tabPanel, this);
36232 * Returns true if this tab is the active tab.
36233 * @return {Boolean}
36235 isActive : function(){
36236 return this.tabPanel.getActiveTab() == this;
36240 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
36243 this.pnode.removeClass("active");
36245 this.fireEvent("deactivate", this.tabPanel, this);
36248 hideAction : function(){
36249 this.bodyEl.hide();
36250 this.bodyEl.setStyle("position", "absolute");
36251 this.bodyEl.setLeft("-20000px");
36252 this.bodyEl.setTop("-20000px");
36255 showAction : function(){
36256 this.bodyEl.setStyle("position", "relative");
36257 this.bodyEl.setTop("");
36258 this.bodyEl.setLeft("");
36259 this.bodyEl.show();
36263 * Set the tooltip for the tab.
36264 * @param {String} tooltip The tab's tooltip
36266 setTooltip : function(text){
36267 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
36268 this.textEl.dom.qtip = text;
36269 this.textEl.dom.removeAttribute('title');
36271 this.textEl.dom.title = text;
36275 onTabClick : function(e){
36276 e.preventDefault();
36277 this.tabPanel.activate(this.id);
36280 onTabMouseDown : function(e){
36281 e.preventDefault();
36282 this.tabPanel.activate(this.id);
36285 getWidth : function(){
36286 return this.inner.getWidth();
36289 setWidth : function(width){
36290 var iwidth = width - this.pnode.getPadding("lr");
36291 this.inner.setWidth(iwidth);
36292 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
36293 this.pnode.setWidth(width);
36297 * Show or hide the tab
36298 * @param {Boolean} hidden True to hide or false to show.
36300 setHidden : function(hidden){
36301 this.hidden = hidden;
36302 this.pnode.setStyle("display", hidden ? "none" : "");
36306 * Returns true if this tab is "hidden"
36307 * @return {Boolean}
36309 isHidden : function(){
36310 return this.hidden;
36314 * Returns the text for this tab
36317 getText : function(){
36321 autoSize : function(){
36322 //this.el.beginMeasure();
36323 this.textEl.setWidth(1);
36325 * #2804 [new] Tabs in Roojs
36326 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
36328 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
36329 //this.el.endMeasure();
36333 * Sets the text for the tab (Note: this also sets the tooltip text)
36334 * @param {String} text The tab's text and tooltip
36336 setText : function(text){
36338 this.textEl.update(text);
36339 this.setTooltip(text);
36340 //if(!this.tabPanel.resizeTabs){
36341 // this.autoSize();
36345 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
36347 activate : function(){
36348 this.tabPanel.activate(this.id);
36352 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
36354 disable : function(){
36355 if(this.tabPanel.active != this){
36356 this.disabled = true;
36357 this.pnode.addClass("disabled");
36362 * Enables this TabPanelItem if it was previously disabled.
36364 enable : function(){
36365 this.disabled = false;
36366 this.pnode.removeClass("disabled");
36370 * Sets the content for this TabPanelItem.
36371 * @param {String} content The content
36372 * @param {Boolean} loadScripts true to look for and load scripts
36374 setContent : function(content, loadScripts){
36375 this.bodyEl.update(content, loadScripts);
36379 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
36380 * @return {Roo.UpdateManager} The UpdateManager
36382 getUpdateManager : function(){
36383 return this.bodyEl.getUpdateManager();
36387 * Set a URL to be used to load the content for this TabPanelItem.
36388 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
36389 * @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)
36390 * @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)
36391 * @return {Roo.UpdateManager} The UpdateManager
36393 setUrl : function(url, params, loadOnce){
36394 if(this.refreshDelegate){
36395 this.un('activate', this.refreshDelegate);
36397 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36398 this.on("activate", this.refreshDelegate);
36399 return this.bodyEl.getUpdateManager();
36403 _handleRefresh : function(url, params, loadOnce){
36404 if(!loadOnce || !this.loaded){
36405 var updater = this.bodyEl.getUpdateManager();
36406 updater.update(url, params, this._setLoaded.createDelegate(this));
36411 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
36412 * Will fail silently if the setUrl method has not been called.
36413 * This does not activate the panel, just updates its content.
36415 refresh : function(){
36416 if(this.refreshDelegate){
36417 this.loaded = false;
36418 this.refreshDelegate();
36423 _setLoaded : function(){
36424 this.loaded = true;
36428 closeClick : function(e){
36431 this.fireEvent("beforeclose", this, o);
36432 if(o.cancel !== true){
36433 this.tabPanel.removeTab(this.id);
36437 * The text displayed in the tooltip for the close icon.
36440 closeText : "Close this tab"