2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = (
9 Roo.each(document.styleSheets[0], function(s) {
10 if (s.href.match(/css-bootstrap4/)) {
18 * base class for bootstrap elements.
22 Roo.bootstrap = Roo.bootstrap || {};
24 * @class Roo.bootstrap.Component
25 * @extends Roo.Component
26 * Bootstrap Component base class
27 * @cfg {String} cls css class
28 * @cfg {String} style any extra css
29 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
31 * @cfg {string} dataId cutomer id
32 * @cfg {string} name Specifies name attribute
33 * @cfg {string} tooltip Text for the tooltip
34 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
35 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
38 * Do not use directly - it does not do anything..
39 * @param {Object} config The config object
44 Roo.bootstrap.Component = function(config){
45 Roo.bootstrap.Component.superclass.constructor.call(this, config);
49 * @event childrenrendered
50 * Fires when the children have been rendered..
51 * @param {Roo.bootstrap.Component} this
53 "childrenrendered" : true
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
65 allowDomMove : false, // to stop relocations in parent onRender...
75 * Initialize Events for the element
77 initEvents : function() { },
83 can_build_overlaid : true,
85 container_method : false,
92 // returns the parent component..
93 return Roo.ComponentMgr.get(this.parentId)
99 onRender : function(ct, position)
101 // Roo.log("Call onRender: " + this.xtype);
103 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
106 if (this.el.attr('xtype')) {
107 this.el.attr('xtypex', this.el.attr('xtype'));
108 this.el.dom.removeAttribute('xtype');
118 var cfg = Roo.apply({}, this.getAutoCreate());
120 cfg.id = this.id || Roo.id();
122 // fill in the extra attributes
123 if (this.xattr && typeof(this.xattr) =='object') {
124 for (var i in this.xattr) {
125 cfg[i] = this.xattr[i];
130 cfg.dataId = this.dataId;
134 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
137 if (this.style) { // fixme needs to support more complex style data.
138 cfg.style = this.style;
142 cfg.name = this.name;
145 this.el = ct.createChild(cfg, position);
148 this.tooltipEl().attr('tooltip', this.tooltip);
151 if(this.tabIndex !== undefined){
152 this.el.dom.setAttribute('tabIndex', this.tabIndex);
159 * Fetch the element to add children to
160 * @return {Roo.Element} defaults to this.el
162 getChildContainer : function()
167 * Fetch the element to display the tooltip on.
168 * @return {Roo.Element} defaults to this.el
170 tooltipEl : function()
175 addxtype : function(tree,cntr)
179 cn = Roo.factory(tree);
180 //Roo.log(['addxtype', cn]);
182 cn.parentType = this.xtype; //??
183 cn.parentId = this.id;
185 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186 if (typeof(cn.container_method) == 'string') {
187 cntr = cn.container_method;
191 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
193 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
195 var build_from_html = Roo.XComponent.build_from_html;
197 var is_body = (tree.xtype == 'Body') ;
199 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
201 var self_cntr_el = Roo.get(this[cntr](false));
203 // do not try and build conditional elements
204 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
208 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210 return this.addxtypeChild(tree,cntr, is_body);
213 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
216 return this.addxtypeChild(Roo.apply({}, tree),cntr);
219 Roo.log('skipping render');
225 if (!build_from_html) {
229 // this i think handles overlaying multiple children of the same type
230 // with the sam eelement.. - which might be buggy..
232 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
238 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
242 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
249 addxtypeChild : function (tree, cntr, is_body)
251 Roo.debug && Roo.log('addxtypeChild:' + cntr);
253 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
256 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257 (typeof(tree['flexy:foreach']) != 'undefined');
261 skip_children = false;
262 // render the element if it's not BODY.
265 // if parent was disabled, then do not try and create the children..
266 if(!this[cntr](true)){
271 cn = Roo.factory(tree);
273 cn.parentType = this.xtype; //??
274 cn.parentId = this.id;
276 var build_from_html = Roo.XComponent.build_from_html;
279 // does the container contain child eleemnts with 'xtype' attributes.
280 // that match this xtype..
281 // note - when we render we create these as well..
282 // so we should check to see if body has xtype set.
283 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
285 var self_cntr_el = Roo.get(this[cntr](false));
286 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
288 //Roo.log(Roo.XComponent.build_from_html);
289 //Roo.log("got echild:");
292 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293 // and are not displayed -this causes this to use up the wrong element when matching.
294 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
297 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
304 //echild.dom.removeAttribute('xtype');
306 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307 Roo.debug && Roo.log(self_cntr_el);
308 Roo.debug && Roo.log(echild);
309 Roo.debug && Roo.log(cn);
315 // if object has flexy:if - then it may or may not be rendered.
316 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
317 // skip a flexy if element.
318 Roo.debug && Roo.log('skipping render');
319 Roo.debug && Roo.log(tree);
321 Roo.debug && Roo.log('skipping all children');
322 skip_children = true;
327 // actually if flexy:foreach is found, we really want to create
328 // multiple copies here...
330 //Roo.log(this[cntr]());
331 // some elements do not have render methods.. like the layouts...
333 if(this[cntr](true) === false){
338 cn.render && cn.render(this[cntr](true));
341 // then add the element..
348 if (typeof (tree.menu) != 'undefined') {
349 tree.menu.parentType = cn.xtype;
350 tree.menu.triggerEl = cn.el;
351 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
355 if (!tree.items || !tree.items.length) {
357 //Roo.log(["no children", this]);
362 var items = tree.items;
365 //Roo.log(items.length);
367 if (!skip_children) {
368 for(var i =0;i < items.length;i++) {
369 // Roo.log(['add child', items[i]]);
370 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
376 //Roo.log("fire childrenrendered");
378 cn.fireEvent('childrenrendered', this);
384 * Set the element that will be used to show or hide
386 setVisibilityEl : function(el)
388 this.visibilityEl = el;
392 * Get the element that will be used to show or hide
394 getVisibilityEl : function()
396 if (typeof(this.visibilityEl) == 'object') {
397 return this.visibilityEl;
400 if (typeof(this.visibilityEl) == 'string') {
401 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
408 * Show a component - removes 'hidden' class
412 if(!this.getVisibilityEl()){
416 this.getVisibilityEl().removeClass('hidden');
418 this.fireEvent('show', this);
423 * Hide a component - adds 'hidden' class
427 if(!this.getVisibilityEl()){
431 this.getVisibilityEl().addClass('hidden');
433 this.fireEvent('hide', this);
446 * @class Roo.bootstrap.Body
447 * @extends Roo.bootstrap.Component
448 * Bootstrap Body class
452 * @param {Object} config The config object
455 Roo.bootstrap.Body = function(config){
457 config = config || {};
459 Roo.bootstrap.Body.superclass.constructor.call(this, config);
460 this.el = Roo.get(config.el ? config.el : document.body );
461 if (this.cls && this.cls.length) {
462 Roo.get(document.body).addClass(this.cls);
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
468 is_body : true,// just to make sure it's constructed?
473 onRender : function(ct, position)
475 /* Roo.log("Roo.bootstrap.Body - onRender");
476 if (this.cls && this.cls.length) {
477 Roo.get(document.body).addClass(this.cls);
496 * @class Roo.bootstrap.ButtonGroup
497 * @extends Roo.bootstrap.Component
498 * Bootstrap ButtonGroup class
499 * @cfg {String} size lg | sm | xs (default empty normal)
500 * @cfg {String} align vertical | justified (default none)
501 * @cfg {String} direction up | down (default down)
502 * @cfg {Boolean} toolbar false | true
503 * @cfg {Boolean} btn true | false
508 * @param {Object} config The config object
511 Roo.bootstrap.ButtonGroup = function(config){
512 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
523 getAutoCreate : function(){
529 cfg.html = this.html || cfg.html;
540 if (['vertical','justified'].indexOf(this.align)!==-1) {
541 cfg.cls = 'btn-group-' + this.align;
543 if (this.align == 'justified') {
544 console.log(this.items);
548 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549 cfg.cls += ' btn-group-' + this.size;
552 if (this.direction == 'up') {
553 cfg.cls += ' dropup' ;
569 * @class Roo.bootstrap.Button
570 * @extends Roo.bootstrap.Component
571 * Bootstrap Button class
572 * @cfg {String} html The button content
573 * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default
574 * @cfg {String} size ( lg | sm | xs)
575 * @cfg {String} tag ( a | input | submit)
576 * @cfg {String} href empty or href
577 * @cfg {Boolean} disabled default false;
578 * @cfg {Boolean} isClose default false;
579 * @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)
580 * @cfg {String} badge text for badge
581 * @cfg {String} theme (default|glow)
582 * @cfg {Boolean} inverse dark themed version
583 * @cfg {Boolean} toggle is it a slidy toggle button
584 * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
585 * @cfg {String} ontext text for on slidy toggle state
586 * @cfg {String} offtext text for off slidy toggle state
587 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
588 * @cfg {Boolean} removeClass remove the standard class..
589 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
592 * Create a new button
593 * @param {Object} config The config object
597 Roo.bootstrap.Button = function(config){
598 Roo.bootstrap.Button.superclass.constructor.call(this, config);
599 this.weightClass = ["btn-default",
611 * When a butotn is pressed
612 * @param {Roo.bootstrap.Button} btn
613 * @param {Roo.EventObject} e
618 * After the button has been toggles
619 * @param {Roo.bootstrap.Button} btn
620 * @param {Roo.EventObject} e
621 * @param {boolean} pressed (also available as button.pressed)
627 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
645 preventDefault: true,
653 getAutoCreate : function(){
661 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
662 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
667 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
669 if (this.toggle == true) {
672 cls: 'slider-frame roo-button',
677 'data-off-text':'OFF',
678 cls: 'slider-button',
684 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
685 cfg.cls += ' '+this.weight;
694 cfg["aria-hidden"] = true;
696 cfg.html = "×";
702 if (this.theme==='default') {
703 cfg.cls = 'btn roo-button';
705 //if (this.parentType != 'Navbar') {
706 this.weight = this.weight.length ? this.weight : 'default';
708 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
710 cfg.cls += ' btn-' + this.weight;
712 } else if (this.theme==='glow') {
715 cfg.cls = 'btn-glow roo-button';
717 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
719 cfg.cls += ' ' + this.weight;
725 this.cls += ' inverse';
729 if (this.active || this.pressed === true) {
730 cfg.cls += ' active';
734 cfg.disabled = 'disabled';
738 Roo.log('changing to ul' );
740 this.glyphicon = 'caret';
743 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
745 //gsRoo.log(this.parentType);
746 if (this.parentType === 'Navbar' && !this.parent().bar) {
747 Roo.log('changing to li?');
756 href : this.href || '#'
759 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
760 cfg.cls += ' dropdown';
767 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
769 if (this.glyphicon) {
770 cfg.html = ' ' + cfg.html;
775 cls: 'glyphicon glyphicon-' + this.glyphicon
785 // cfg.cls='btn roo-button';
789 var value = cfg.html;
794 cls: 'glyphicon glyphicon-' + this.glyphicon,
813 cfg.cls += ' dropdown';
814 cfg.html = typeof(cfg.html) != 'undefined' ?
815 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
818 if (cfg.tag !== 'a' && this.href !== '') {
819 throw "Tag must be a to set href.";
820 } else if (this.href.length > 0) {
821 cfg.href = this.href;
824 if(this.removeClass){
829 cfg.target = this.target;
834 initEvents: function() {
835 // Roo.log('init events?');
836 // Roo.log(this.el.dom);
839 if (typeof (this.menu) != 'undefined') {
840 this.menu.parentType = this.xtype;
841 this.menu.triggerEl = this.el;
842 this.addxtype(Roo.apply({}, this.menu));
846 if (this.el.hasClass('roo-button')) {
847 this.el.on('click', this.onClick, this);
849 this.el.select('.roo-button').on('click', this.onClick, this);
852 if(this.removeClass){
853 this.el.on('click', this.onClick, this);
856 this.el.enableDisplayMode();
859 onClick : function(e)
865 Roo.log('button on click ');
866 if(this.preventDefault){
870 if (this.pressed === true || this.pressed === false) {
871 this.toggleActive(e);
875 this.fireEvent('click', this, e);
879 * Enables this button
883 this.disabled = false;
884 this.el.removeClass('disabled');
888 * Disable this button
892 this.disabled = true;
893 this.el.addClass('disabled');
896 * sets the active state on/off,
897 * @param {Boolean} state (optional) Force a particular state
899 setActive : function(v) {
901 this.el[v ? 'addClass' : 'removeClass']('active');
905 * toggles the current active state
907 toggleActive : function(e)
909 this.setActive(!this.pressed);
910 this.fireEvent('toggle', this, e, !this.pressed);
913 * get the current active state
914 * @return {boolean} true if it's active
916 isActive : function()
918 return this.el.hasClass('active');
921 * set the text of the first selected button
923 setText : function(str)
925 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
928 * get the text of the first selected button
932 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
935 setWeight : function(str)
937 this.el.removeClass(this.weightClass);
938 this.el.addClass('btn-' + str);
952 * @class Roo.bootstrap.Column
953 * @extends Roo.bootstrap.Component
954 * Bootstrap Column class
955 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
956 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
957 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
958 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
959 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
960 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
961 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
962 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
965 * @cfg {Boolean} hidden (true|false) hide the element
966 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
967 * @cfg {String} fa (ban|check|...) font awesome icon
968 * @cfg {Number} fasize (1|2|....) font awsome size
970 * @cfg {String} icon (info-sign|check|...) glyphicon name
972 * @cfg {String} html content of column.
975 * Create a new Column
976 * @param {Object} config The config object
979 Roo.bootstrap.Column = function(config){
980 Roo.bootstrap.Column.superclass.constructor.call(this, config);
983 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1001 getAutoCreate : function(){
1002 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1010 ['xs','sm','md','lg'].map(function(size){
1011 //Roo.log( size + ':' + settings[size]);
1013 if (settings[size+'off'] !== false) {
1014 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1017 if (settings[size] === false) {
1021 if (!settings[size]) { // 0 = hidden
1022 cfg.cls += ' hidden-' + size;
1025 cfg.cls += ' col-' + size + '-' + settings[size];
1030 cfg.cls += ' hidden';
1033 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1034 cfg.cls +=' alert alert-' + this.alert;
1038 if (this.html.length) {
1039 cfg.html = this.html;
1043 if (this.fasize > 1) {
1044 fasize = ' fa-' + this.fasize + 'x';
1046 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1051 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1070 * @class Roo.bootstrap.Container
1071 * @extends Roo.bootstrap.Component
1072 * Bootstrap Container class
1073 * @cfg {Boolean} jumbotron is it a jumbotron element
1074 * @cfg {String} html content of element
1075 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1076 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1077 * @cfg {String} header content of header (for panel)
1078 * @cfg {String} footer content of footer (for panel)
1079 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1080 * @cfg {String} tag (header|aside|section) type of HTML tag.
1081 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1082 * @cfg {String} fa font awesome icon
1083 * @cfg {String} icon (info-sign|check|...) glyphicon name
1084 * @cfg {Boolean} hidden (true|false) hide the element
1085 * @cfg {Boolean} expandable (true|false) default false
1086 * @cfg {Boolean} expanded (true|false) default true
1087 * @cfg {String} rheader contet on the right of header
1088 * @cfg {Boolean} clickable (true|false) default false
1092 * Create a new Container
1093 * @param {Object} config The config object
1096 Roo.bootstrap.Container = function(config){
1097 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1103 * After the panel has been expand
1105 * @param {Roo.bootstrap.Container} this
1110 * After the panel has been collapsed
1112 * @param {Roo.bootstrap.Container} this
1117 * When a element is chick
1118 * @param {Roo.bootstrap.Container} this
1119 * @param {Roo.EventObject} e
1125 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1143 getChildContainer : function() {
1149 if (this.panel.length) {
1150 return this.el.select('.panel-body',true).first();
1157 getAutoCreate : function(){
1160 tag : this.tag || 'div',
1164 if (this.jumbotron) {
1165 cfg.cls = 'jumbotron';
1170 // - this is applied by the parent..
1172 // cfg.cls = this.cls + '';
1175 if (this.sticky.length) {
1177 var bd = Roo.get(document.body);
1178 if (!bd.hasClass('bootstrap-sticky')) {
1179 bd.addClass('bootstrap-sticky');
1180 Roo.select('html',true).setStyle('height', '100%');
1183 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1187 if (this.well.length) {
1188 switch (this.well) {
1191 cfg.cls +=' well well-' +this.well;
1200 cfg.cls += ' hidden';
1204 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1205 cfg.cls +=' alert alert-' + this.alert;
1210 if (this.panel.length) {
1211 cfg.cls += ' panel panel-' + this.panel;
1213 if (this.header.length) {
1217 if(this.expandable){
1219 cfg.cls = cfg.cls + ' expandable';
1223 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1231 cls : 'panel-title',
1232 html : (this.expandable ? ' ' : '') + this.header
1236 cls: 'panel-header-right',
1242 cls : 'panel-heading',
1243 style : this.expandable ? 'cursor: pointer' : '',
1251 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1256 if (this.footer.length) {
1258 cls : 'panel-footer',
1267 body.html = this.html || cfg.html;
1268 // prefix with the icons..
1270 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1273 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1278 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1279 cfg.cls = 'container';
1285 initEvents: function()
1287 if(this.expandable){
1288 var headerEl = this.headerEl();
1291 headerEl.on('click', this.onToggleClick, this);
1296 this.el.on('click', this.onClick, this);
1301 onToggleClick : function()
1303 var headerEl = this.headerEl();
1319 if(this.fireEvent('expand', this)) {
1321 this.expanded = true;
1323 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1325 this.el.select('.panel-body',true).first().removeClass('hide');
1327 var toggleEl = this.toggleEl();
1333 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1338 collapse : function()
1340 if(this.fireEvent('collapse', this)) {
1342 this.expanded = false;
1344 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1345 this.el.select('.panel-body',true).first().addClass('hide');
1347 var toggleEl = this.toggleEl();
1353 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1357 toggleEl : function()
1359 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1363 return this.el.select('.panel-heading .fa',true).first();
1366 headerEl : function()
1368 if(!this.el || !this.panel.length || !this.header.length){
1372 return this.el.select('.panel-heading',true).first()
1377 if(!this.el || !this.panel.length){
1381 return this.el.select('.panel-body',true).first()
1384 titleEl : function()
1386 if(!this.el || !this.panel.length || !this.header.length){
1390 return this.el.select('.panel-title',true).first();
1393 setTitle : function(v)
1395 var titleEl = this.titleEl();
1401 titleEl.dom.innerHTML = v;
1404 getTitle : function()
1407 var titleEl = this.titleEl();
1413 return titleEl.dom.innerHTML;
1416 setRightTitle : function(v)
1418 var t = this.el.select('.panel-header-right',true).first();
1424 t.dom.innerHTML = v;
1427 onClick : function(e)
1431 this.fireEvent('click', this, e);
1444 * @class Roo.bootstrap.Img
1445 * @extends Roo.bootstrap.Component
1446 * Bootstrap Img class
1447 * @cfg {Boolean} imgResponsive false | true
1448 * @cfg {String} border rounded | circle | thumbnail
1449 * @cfg {String} src image source
1450 * @cfg {String} alt image alternative text
1451 * @cfg {String} href a tag href
1452 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1453 * @cfg {String} xsUrl xs image source
1454 * @cfg {String} smUrl sm image source
1455 * @cfg {String} mdUrl md image source
1456 * @cfg {String} lgUrl lg image source
1459 * Create a new Input
1460 * @param {Object} config The config object
1463 Roo.bootstrap.Img = function(config){
1464 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1470 * The img click event for the img.
1471 * @param {Roo.EventObject} e
1477 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1479 imgResponsive: true,
1489 getAutoCreate : function()
1491 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1492 return this.createSingleImg();
1497 cls: 'roo-image-responsive-group',
1502 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1504 if(!_this[size + 'Url']){
1510 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1511 html: _this.html || cfg.html,
1512 src: _this[size + 'Url']
1515 img.cls += ' roo-image-responsive-' + size;
1517 var s = ['xs', 'sm', 'md', 'lg'];
1519 s.splice(s.indexOf(size), 1);
1521 Roo.each(s, function(ss){
1522 img.cls += ' hidden-' + ss;
1525 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1526 cfg.cls += ' img-' + _this.border;
1530 cfg.alt = _this.alt;
1543 a.target = _this.target;
1547 cfg.cn.push((_this.href) ? a : img);
1554 createSingleImg : function()
1558 cls: (this.imgResponsive) ? 'img-responsive' : '',
1560 src : 'about:blank' // just incase src get's set to undefined?!?
1563 cfg.html = this.html || cfg.html;
1565 cfg.src = this.src || cfg.src;
1567 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1568 cfg.cls += ' img-' + this.border;
1585 a.target = this.target;
1590 return (this.href) ? a : cfg;
1593 initEvents: function()
1596 this.el.on('click', this.onClick, this);
1601 onClick : function(e)
1603 Roo.log('img onclick');
1604 this.fireEvent('click', this, e);
1607 * Sets the url of the image - used to update it
1608 * @param {String} url the url of the image
1611 setSrc : function(url)
1615 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1616 this.el.dom.src = url;
1620 this.el.select('img', true).first().dom.src = url;
1636 * @class Roo.bootstrap.Link
1637 * @extends Roo.bootstrap.Component
1638 * Bootstrap Link Class
1639 * @cfg {String} alt image alternative text
1640 * @cfg {String} href a tag href
1641 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1642 * @cfg {String} html the content of the link.
1643 * @cfg {String} anchor name for the anchor link
1644 * @cfg {String} fa - favicon
1646 * @cfg {Boolean} preventDefault (true | false) default false
1650 * Create a new Input
1651 * @param {Object} config The config object
1654 Roo.bootstrap.Link = function(config){
1655 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1661 * The img click event for the img.
1662 * @param {Roo.EventObject} e
1668 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1672 preventDefault: false,
1678 getAutoCreate : function()
1680 var html = this.html || '';
1682 if (this.fa !== false) {
1683 html = '<i class="fa fa-' + this.fa + '"></i>';
1688 // anchor's do not require html/href...
1689 if (this.anchor === false) {
1691 cfg.href = this.href || '#';
1693 cfg.name = this.anchor;
1694 if (this.html !== false || this.fa !== false) {
1697 if (this.href !== false) {
1698 cfg.href = this.href;
1702 if(this.alt !== false){
1707 if(this.target !== false) {
1708 cfg.target = this.target;
1714 initEvents: function() {
1716 if(!this.href || this.preventDefault){
1717 this.el.on('click', this.onClick, this);
1721 onClick : function(e)
1723 if(this.preventDefault){
1726 //Roo.log('img onclick');
1727 this.fireEvent('click', this, e);
1740 * @class Roo.bootstrap.Header
1741 * @extends Roo.bootstrap.Component
1742 * Bootstrap Header class
1743 * @cfg {String} html content of header
1744 * @cfg {Number} level (1|2|3|4|5|6) default 1
1747 * Create a new Header
1748 * @param {Object} config The config object
1752 Roo.bootstrap.Header = function(config){
1753 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1756 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1764 getAutoCreate : function(){
1769 tag: 'h' + (1 *this.level),
1770 html: this.html || ''
1782 * Ext JS Library 1.1.1
1783 * Copyright(c) 2006-2007, Ext JS, LLC.
1785 * Originally Released Under LGPL - original licence link has changed is not relivant.
1788 * <script type="text/javascript">
1792 * @class Roo.bootstrap.MenuMgr
1793 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1796 Roo.bootstrap.MenuMgr = function(){
1797 var menus, active, groups = {}, attached = false, lastShow = new Date();
1799 // private - called when first menu is created
1802 active = new Roo.util.MixedCollection();
1803 Roo.get(document).addKeyListener(27, function(){
1804 if(active.length > 0){
1812 if(active && active.length > 0){
1813 var c = active.clone();
1823 if(active.length < 1){
1824 Roo.get(document).un("mouseup", onMouseDown);
1832 var last = active.last();
1833 lastShow = new Date();
1836 Roo.get(document).on("mouseup", onMouseDown);
1841 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1842 m.parentMenu.activeChild = m;
1843 }else if(last && last.isVisible()){
1844 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1849 function onBeforeHide(m){
1851 m.activeChild.hide();
1853 if(m.autoHideTimer){
1854 clearTimeout(m.autoHideTimer);
1855 delete m.autoHideTimer;
1860 function onBeforeShow(m){
1861 var pm = m.parentMenu;
1862 if(!pm && !m.allowOtherMenus){
1864 }else if(pm && pm.activeChild && active != m){
1865 pm.activeChild.hide();
1869 // private this should really trigger on mouseup..
1870 function onMouseDown(e){
1871 Roo.log("on Mouse Up");
1873 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1874 Roo.log("MenuManager hideAll");
1883 function onBeforeCheck(mi, state){
1885 var g = groups[mi.group];
1886 for(var i = 0, l = g.length; i < l; i++){
1888 g[i].setChecked(false);
1897 * Hides all menus that are currently visible
1899 hideAll : function(){
1904 register : function(menu){
1908 menus[menu.id] = menu;
1909 menu.on("beforehide", onBeforeHide);
1910 menu.on("hide", onHide);
1911 menu.on("beforeshow", onBeforeShow);
1912 menu.on("show", onShow);
1914 if(g && menu.events["checkchange"]){
1918 groups[g].push(menu);
1919 menu.on("checkchange", onCheck);
1924 * Returns a {@link Roo.menu.Menu} object
1925 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1926 * be used to generate and return a new Menu instance.
1928 get : function(menu){
1929 if(typeof menu == "string"){ // menu id
1931 }else if(menu.events){ // menu instance
1934 /*else if(typeof menu.length == 'number'){ // array of menu items?
1935 return new Roo.bootstrap.Menu({items:menu});
1936 }else{ // otherwise, must be a config
1937 return new Roo.bootstrap.Menu(menu);
1944 unregister : function(menu){
1945 delete menus[menu.id];
1946 menu.un("beforehide", onBeforeHide);
1947 menu.un("hide", onHide);
1948 menu.un("beforeshow", onBeforeShow);
1949 menu.un("show", onShow);
1951 if(g && menu.events["checkchange"]){
1952 groups[g].remove(menu);
1953 menu.un("checkchange", onCheck);
1958 registerCheckable : function(menuItem){
1959 var g = menuItem.group;
1964 groups[g].push(menuItem);
1965 menuItem.on("beforecheckchange", onBeforeCheck);
1970 unregisterCheckable : function(menuItem){
1971 var g = menuItem.group;
1973 groups[g].remove(menuItem);
1974 menuItem.un("beforecheckchange", onBeforeCheck);
1986 * @class Roo.bootstrap.Menu
1987 * @extends Roo.bootstrap.Component
1988 * Bootstrap Menu class - container for MenuItems
1989 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1990 * @cfg {bool} hidden if the menu should be hidden when rendered.
1991 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1992 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1996 * @param {Object} config The config object
2000 Roo.bootstrap.Menu = function(config){
2001 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2002 if (this.registerMenu && this.type != 'treeview') {
2003 Roo.bootstrap.MenuMgr.register(this);
2008 * Fires before this menu is displayed
2009 * @param {Roo.menu.Menu} this
2014 * Fires before this menu is hidden
2015 * @param {Roo.menu.Menu} this
2020 * Fires after this menu is displayed
2021 * @param {Roo.menu.Menu} this
2026 * Fires after this menu is hidden
2027 * @param {Roo.menu.Menu} this
2032 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2033 * @param {Roo.menu.Menu} this
2034 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2035 * @param {Roo.EventObject} e
2040 * Fires when the mouse is hovering over this menu
2041 * @param {Roo.menu.Menu} this
2042 * @param {Roo.EventObject} e
2043 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2048 * Fires when the mouse exits this menu
2049 * @param {Roo.menu.Menu} this
2050 * @param {Roo.EventObject} e
2051 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2056 * Fires when a menu item contained in this menu is clicked
2057 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2058 * @param {Roo.EventObject} e
2062 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2065 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2069 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2072 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2074 registerMenu : true,
2076 menuItems :false, // stores the menu items..
2086 getChildContainer : function() {
2090 getAutoCreate : function(){
2092 //if (['right'].indexOf(this.align)!==-1) {
2093 // cfg.cn[1].cls += ' pull-right'
2099 cls : 'dropdown-menu' ,
2100 style : 'z-index:1000'
2104 if (this.type === 'submenu') {
2105 cfg.cls = 'submenu active';
2107 if (this.type === 'treeview') {
2108 cfg.cls = 'treeview-menu';
2113 initEvents : function() {
2115 // Roo.log("ADD event");
2116 // Roo.log(this.triggerEl.dom);
2118 this.triggerEl.on('click', this.onTriggerClick, this);
2120 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2122 this.triggerEl.addClass('dropdown-toggle');
2125 this.el.on('touchstart' , this.onTouch, this);
2127 this.el.on('click' , this.onClick, this);
2129 this.el.on("mouseover", this.onMouseOver, this);
2130 this.el.on("mouseout", this.onMouseOut, this);
2134 findTargetItem : function(e)
2136 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2140 //Roo.log(t); Roo.log(t.id);
2142 //Roo.log(this.menuitems);
2143 return this.menuitems.get(t.id);
2145 //return this.items.get(t.menuItemId);
2151 onTouch : function(e)
2153 Roo.log("menu.onTouch");
2154 //e.stopEvent(); this make the user popdown broken
2158 onClick : function(e)
2160 Roo.log("menu.onClick");
2162 var t = this.findTargetItem(e);
2163 if(!t || t.isContainer){
2168 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2169 if(t == this.activeItem && t.shouldDeactivate(e)){
2170 this.activeItem.deactivate();
2171 delete this.activeItem;
2175 this.setActiveItem(t, true);
2183 Roo.log('pass click event');
2187 this.fireEvent("click", this, t, e);
2191 if(!t.href.length || t.href == '#'){
2192 (function() { _this.hide(); }).defer(100);
2197 onMouseOver : function(e){
2198 var t = this.findTargetItem(e);
2201 // if(t.canActivate && !t.disabled){
2202 // this.setActiveItem(t, true);
2206 this.fireEvent("mouseover", this, e, t);
2208 isVisible : function(){
2209 return !this.hidden;
2211 onMouseOut : function(e){
2212 var t = this.findTargetItem(e);
2215 // if(t == this.activeItem && t.shouldDeactivate(e)){
2216 // this.activeItem.deactivate();
2217 // delete this.activeItem;
2220 this.fireEvent("mouseout", this, e, t);
2225 * Displays this menu relative to another element
2226 * @param {String/HTMLElement/Roo.Element} element The element to align to
2227 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2228 * the element (defaults to this.defaultAlign)
2229 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2231 show : function(el, pos, parentMenu){
2232 this.parentMenu = parentMenu;
2236 this.fireEvent("beforeshow", this);
2237 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2240 * Displays this menu at a specific xy position
2241 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2242 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2244 showAt : function(xy, parentMenu, /* private: */_e){
2245 this.parentMenu = parentMenu;
2250 this.fireEvent("beforeshow", this);
2251 //xy = this.el.adjustForConstraints(xy);
2255 this.hideMenuItems();
2256 this.hidden = false;
2257 this.triggerEl.addClass('open');
2259 // reassign x when hitting right
2260 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2261 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2264 // reassign y when hitting bottom
2265 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2266 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2269 // but the list may align on trigger left or trigger top... should it be a properity?
2271 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2276 this.fireEvent("show", this);
2282 this.doFocus.defer(50, this);
2286 doFocus : function(){
2288 this.focusEl.focus();
2293 * Hides this menu and optionally all parent menus
2294 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2296 hide : function(deep)
2299 this.hideMenuItems();
2300 if(this.el && this.isVisible()){
2301 this.fireEvent("beforehide", this);
2302 if(this.activeItem){
2303 this.activeItem.deactivate();
2304 this.activeItem = null;
2306 this.triggerEl.removeClass('open');;
2308 this.fireEvent("hide", this);
2310 if(deep === true && this.parentMenu){
2311 this.parentMenu.hide(true);
2315 onTriggerClick : function(e)
2317 Roo.log('trigger click');
2319 var target = e.getTarget();
2321 Roo.log(target.nodeName.toLowerCase());
2323 if(target.nodeName.toLowerCase() === 'i'){
2329 onTriggerPress : function(e)
2331 Roo.log('trigger press');
2332 //Roo.log(e.getTarget());
2333 // Roo.log(this.triggerEl.dom);
2335 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2336 var pel = Roo.get(e.getTarget());
2337 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2338 Roo.log('is treeview or dropdown?');
2342 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2346 if (this.isVisible()) {
2351 this.show(this.triggerEl, false, false);
2354 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2361 hideMenuItems : function()
2363 Roo.log("hide Menu Items");
2367 //$(backdrop).remove()
2368 this.el.select('.open',true).each(function(aa) {
2370 aa.removeClass('open');
2371 //var parent = getParent($(this))
2372 //var relatedTarget = { relatedTarget: this }
2374 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2375 //if (e.isDefaultPrevented()) return
2376 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2379 addxtypeChild : function (tree, cntr) {
2380 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2382 this.menuitems.add(comp);
2394 this.getEl().dom.innerHTML = '';
2395 this.menuitems.clear();
2409 * @class Roo.bootstrap.MenuItem
2410 * @extends Roo.bootstrap.Component
2411 * Bootstrap MenuItem class
2412 * @cfg {String} html the menu label
2413 * @cfg {String} href the link
2414 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2415 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2416 * @cfg {Boolean} active used on sidebars to highlight active itesm
2417 * @cfg {String} fa favicon to show on left of menu item.
2418 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2422 * Create a new MenuItem
2423 * @param {Object} config The config object
2427 Roo.bootstrap.MenuItem = function(config){
2428 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2433 * The raw click event for the entire grid.
2434 * @param {Roo.bootstrap.MenuItem} this
2435 * @param {Roo.EventObject} e
2441 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2445 preventDefault: false,
2446 isContainer : false,
2450 getAutoCreate : function(){
2452 if(this.isContainer){
2455 cls: 'dropdown-menu-item'
2469 if (this.fa !== false) {
2472 cls : 'fa fa-' + this.fa
2481 cls: 'dropdown-menu-item',
2484 if (this.parent().type == 'treeview') {
2485 cfg.cls = 'treeview-menu';
2488 cfg.cls += ' active';
2493 anc.href = this.href || cfg.cn[0].href ;
2494 ctag.html = this.html || cfg.cn[0].html ;
2498 initEvents: function()
2500 if (this.parent().type == 'treeview') {
2501 this.el.select('a').on('click', this.onClick, this);
2505 this.menu.parentType = this.xtype;
2506 this.menu.triggerEl = this.el;
2507 this.menu = this.addxtype(Roo.apply({}, this.menu));
2511 onClick : function(e)
2513 Roo.log('item on click ');
2515 if(this.preventDefault){
2518 //this.parent().hideMenuItems();
2520 this.fireEvent('click', this, e);
2539 * @class Roo.bootstrap.MenuSeparator
2540 * @extends Roo.bootstrap.Component
2541 * Bootstrap MenuSeparator class
2544 * Create a new MenuItem
2545 * @param {Object} config The config object
2549 Roo.bootstrap.MenuSeparator = function(config){
2550 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2553 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2555 getAutoCreate : function(){
2574 * @class Roo.bootstrap.Modal
2575 * @extends Roo.bootstrap.Component
2576 * Bootstrap Modal class
2577 * @cfg {String} title Title of dialog
2578 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2579 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2580 * @cfg {Boolean} specificTitle default false
2581 * @cfg {Array} buttons Array of buttons or standard button set..
2582 * @cfg {String} buttonPosition (left|right|center) default right
2583 * @cfg {Boolean} animate default true
2584 * @cfg {Boolean} allow_close default true
2585 * @cfg {Boolean} fitwindow default false
2586 * @cfg {String} size (sm|lg) default empty
2587 * @cfg {Number} max_width set the max width of modal
2591 * Create a new Modal Dialog
2592 * @param {Object} config The config object
2595 Roo.bootstrap.Modal = function(config){
2596 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2601 * The raw btnclick event for the button
2602 * @param {Roo.EventObject} e
2607 * Fire when dialog resize
2608 * @param {Roo.bootstrap.Modal} this
2609 * @param {Roo.EventObject} e
2613 this.buttons = this.buttons || [];
2616 this.tmpl = Roo.factory(this.tmpl);
2621 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2623 title : 'test dialog',
2633 specificTitle: false,
2635 buttonPosition: 'right',
2658 onRender : function(ct, position)
2660 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2663 var cfg = Roo.apply({}, this.getAutoCreate());
2666 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2668 //if (!cfg.name.length) {
2672 cfg.cls += ' ' + this.cls;
2675 cfg.style = this.style;
2677 this.el = Roo.get(document.body).createChild(cfg, position);
2679 //var type = this.el.dom.type;
2682 if(this.tabIndex !== undefined){
2683 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2686 this.dialogEl = this.el.select('.modal-dialog',true).first();
2687 this.bodyEl = this.el.select('.modal-body',true).first();
2688 this.closeEl = this.el.select('.modal-header .close', true).first();
2689 this.headerEl = this.el.select('.modal-header',true).first();
2690 this.titleEl = this.el.select('.modal-title',true).first();
2691 this.footerEl = this.el.select('.modal-footer',true).first();
2693 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2695 //this.el.addClass("x-dlg-modal");
2697 if (this.buttons.length) {
2698 Roo.each(this.buttons, function(bb) {
2699 var b = Roo.apply({}, bb);
2700 b.xns = b.xns || Roo.bootstrap;
2701 b.xtype = b.xtype || 'Button';
2702 if (typeof(b.listeners) == 'undefined') {
2703 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2706 var btn = Roo.factory(b);
2708 btn.render(this.el.select('.modal-footer div').first());
2712 // render the children.
2715 if(typeof(this.items) != 'undefined'){
2716 var items = this.items;
2719 for(var i =0;i < items.length;i++) {
2720 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2724 this.items = nitems;
2726 // where are these used - they used to be body/close/footer
2730 //this.el.addClass([this.fieldClass, this.cls]);
2734 getAutoCreate : function()
2738 html : this.html || ''
2743 cls : 'modal-title',
2747 if(this.specificTitle){
2753 if (this.allow_close) {
2765 if(this.size.length){
2766 size = 'modal-' + this.size;
2773 cls: "modal-dialog " + size,
2776 cls : "modal-content",
2779 cls : 'modal-header',
2784 cls : 'modal-footer',
2788 cls: 'btn-' + this.buttonPosition
2805 modal.cls += ' fade';
2811 getChildContainer : function() {
2816 getButtonContainer : function() {
2817 return this.el.select('.modal-footer div',true).first();
2820 initEvents : function()
2822 if (this.allow_close) {
2823 this.closeEl.on('click', this.hide, this);
2825 Roo.EventManager.onWindowResize(this.resize, this, true);
2832 this.maskEl.setSize(
2833 Roo.lib.Dom.getViewWidth(true),
2834 Roo.lib.Dom.getViewHeight(true)
2837 if (this.fitwindow) {
2839 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2840 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2845 if(this.max_width !== 0) {
2847 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2850 this.setSize(w, this.height);
2854 if(this.max_height) {
2855 this.setSize(w,Math.min(
2857 Roo.lib.Dom.getViewportHeight(true) - 60
2863 if(!this.fit_content) {
2864 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2868 this.setSize(w, Math.min(
2870 this.headerEl.getHeight() +
2871 this.footerEl.getHeight() +
2872 this.getChildHeight(this.bodyEl.dom.childNodes),
2873 Roo.lib.Dom.getViewportHeight(true) - 60)
2879 setSize : function(w,h)
2890 if (!this.rendered) {
2894 //this.el.setStyle('display', 'block');
2895 this.el.removeClass('hideing');
2896 this.el.addClass('show');
2898 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2901 this.el.addClass('in');
2904 this.el.addClass('in');
2907 // not sure how we can show data in here..
2909 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2912 Roo.get(document.body).addClass("x-body-masked");
2914 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2915 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2916 this.maskEl.addClass('show');
2920 this.fireEvent('show', this);
2922 // set zindex here - otherwise it appears to be ignored...
2923 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2926 this.items.forEach( function(e) {
2927 e.layout ? e.layout() : false;
2935 if(this.fireEvent("beforehide", this) !== false){
2936 this.maskEl.removeClass('show');
2937 Roo.get(document.body).removeClass("x-body-masked");
2938 this.el.removeClass('in');
2939 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2941 if(this.animate){ // why
2942 this.el.addClass('hideing');
2944 if (!this.el.hasClass('hideing')) {
2945 return; // it's been shown again...
2947 this.el.removeClass('show');
2948 this.el.removeClass('hideing');
2952 this.el.removeClass('show');
2954 this.fireEvent('hide', this);
2957 isVisible : function()
2960 return this.el.hasClass('show') && !this.el.hasClass('hideing');
2964 addButton : function(str, cb)
2968 var b = Roo.apply({}, { html : str } );
2969 b.xns = b.xns || Roo.bootstrap;
2970 b.xtype = b.xtype || 'Button';
2971 if (typeof(b.listeners) == 'undefined') {
2972 b.listeners = { click : cb.createDelegate(this) };
2975 var btn = Roo.factory(b);
2977 btn.render(this.el.select('.modal-footer div').first());
2983 setDefaultButton : function(btn)
2985 //this.el.select('.modal-footer').()
2989 resizeTo: function(w,h)
2993 this.dialogEl.setWidth(w);
2994 if (this.diff === false) {
2995 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2998 this.bodyEl.setHeight(h - this.diff);
3000 this.fireEvent('resize', this);
3003 setContentSize : function(w, h)
3007 onButtonClick: function(btn,e)
3010 this.fireEvent('btnclick', btn.name, e);
3013 * Set the title of the Dialog
3014 * @param {String} str new Title
3016 setTitle: function(str) {
3017 this.titleEl.dom.innerHTML = str;
3020 * Set the body of the Dialog
3021 * @param {String} str new Title
3023 setBody: function(str) {
3024 this.bodyEl.dom.innerHTML = str;
3027 * Set the body of the Dialog using the template
3028 * @param {Obj} data - apply this data to the template and replace the body contents.
3030 applyBody: function(obj)
3033 Roo.log("Error - using apply Body without a template");
3036 this.tmpl.overwrite(this.bodyEl, obj);
3039 getChildHeight : function(child_nodes)
3043 child_nodes.length == 0
3048 var child_height = 0;
3050 for(var i = 0; i < child_nodes.length; i++) {
3053 * for modal with tabs...
3054 if(child_nodes[i].classList.contains('roo-layout-panel')) {
3056 var layout_childs = child_nodes[i].childNodes;
3058 for(var j = 0; j < layout_childs.length; j++) {
3060 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3062 var layout_body_childs = layout_childs[j].childNodes;
3064 for(var k = 0; k < layout_body_childs.length; k++) {
3066 if(layout_body_childs[k].classList.contains('navbar')) {
3067 child_height += layout_body_childs[k].offsetHeight;
3071 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3073 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3075 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3077 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3078 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3093 child_height += child_nodes[i].offsetHeight;
3094 // Roo.log(child_nodes[i].offsetHeight);
3097 return child_height;
3103 Roo.apply(Roo.bootstrap.Modal, {
3105 * Button config that displays a single OK button
3114 * Button config that displays Yes and No buttons
3130 * Button config that displays OK and Cancel buttons
3145 * Button config that displays Yes, No and Cancel buttons
3169 * messagebox - can be used as a replace
3173 * @class Roo.MessageBox
3174 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3178 Roo.Msg.alert('Status', 'Changes saved successfully.');
3180 // Prompt for user data:
3181 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3183 // process text value...
3187 // Show a dialog using config options:
3189 title:'Save Changes?',
3190 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3191 buttons: Roo.Msg.YESNOCANCEL,
3198 Roo.bootstrap.MessageBox = function(){
3199 var dlg, opt, mask, waitTimer;
3200 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3201 var buttons, activeTextEl, bwidth;
3205 var handleButton = function(button){
3207 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3211 var handleHide = function(){
3213 dlg.el.removeClass(opt.cls);
3216 // Roo.TaskMgr.stop(waitTimer);
3217 // waitTimer = null;
3222 var updateButtons = function(b){
3225 buttons["ok"].hide();
3226 buttons["cancel"].hide();
3227 buttons["yes"].hide();
3228 buttons["no"].hide();
3229 //dlg.footer.dom.style.display = 'none';
3232 dlg.footerEl.dom.style.display = '';
3233 for(var k in buttons){
3234 if(typeof buttons[k] != "function"){
3237 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3238 width += buttons[k].el.getWidth()+15;
3248 var handleEsc = function(d, k, e){
3249 if(opt && opt.closable !== false){
3259 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3260 * @return {Roo.BasicDialog} The BasicDialog element
3262 getDialog : function(){
3264 dlg = new Roo.bootstrap.Modal( {
3267 //constraintoviewport:false,
3269 //collapsible : false,
3274 //buttonAlign:"center",
3275 closeClick : function(){
3276 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3279 handleButton("cancel");
3284 dlg.on("hide", handleHide);
3286 //dlg.addKeyListener(27, handleEsc);
3288 this.buttons = buttons;
3289 var bt = this.buttonText;
3290 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3291 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3292 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3293 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3295 bodyEl = dlg.bodyEl.createChild({
3297 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3298 '<textarea class="roo-mb-textarea"></textarea>' +
3299 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3301 msgEl = bodyEl.dom.firstChild;
3302 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3303 textboxEl.enableDisplayMode();
3304 textboxEl.addKeyListener([10,13], function(){
3305 if(dlg.isVisible() && opt && opt.buttons){
3308 }else if(opt.buttons.yes){
3309 handleButton("yes");
3313 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3314 textareaEl.enableDisplayMode();
3315 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3316 progressEl.enableDisplayMode();
3318 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3319 var pf = progressEl.dom.firstChild;
3321 pp = Roo.get(pf.firstChild);
3322 pp.setHeight(pf.offsetHeight);
3330 * Updates the message box body text
3331 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3332 * the XHTML-compliant non-breaking space character '&#160;')
3333 * @return {Roo.MessageBox} This message box
3335 updateText : function(text)
3337 if(!dlg.isVisible() && !opt.width){
3338 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3339 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3341 msgEl.innerHTML = text || ' ';
3343 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3344 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3346 Math.min(opt.width || cw , this.maxWidth),
3347 Math.max(opt.minWidth || this.minWidth, bwidth)
3350 activeTextEl.setWidth(w);
3352 if(dlg.isVisible()){
3353 dlg.fixedcenter = false;
3355 // to big, make it scroll. = But as usual stupid IE does not support
3358 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3359 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3360 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3362 bodyEl.dom.style.height = '';
3363 bodyEl.dom.style.overflowY = '';
3366 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3368 bodyEl.dom.style.overflowX = '';
3371 dlg.setContentSize(w, bodyEl.getHeight());
3372 if(dlg.isVisible()){
3373 dlg.fixedcenter = true;
3379 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3380 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3381 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3382 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3383 * @return {Roo.MessageBox} This message box
3385 updateProgress : function(value, text){
3387 this.updateText(text);
3390 if (pp) { // weird bug on my firefox - for some reason this is not defined
3391 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3392 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3398 * Returns true if the message box is currently displayed
3399 * @return {Boolean} True if the message box is visible, else false
3401 isVisible : function(){
3402 return dlg && dlg.isVisible();
3406 * Hides the message box if it is displayed
3409 if(this.isVisible()){
3415 * Displays a new message box, or reinitializes an existing message box, based on the config options
3416 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3417 * The following config object properties are supported:
3419 Property Type Description
3420 ---------- --------------- ------------------------------------------------------------------------------------
3421 animEl String/Element An id or Element from which the message box should animate as it opens and
3422 closes (defaults to undefined)
3423 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3424 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3425 closable Boolean False to hide the top-right close button (defaults to true). Note that
3426 progress and wait dialogs will ignore this property and always hide the
3427 close button as they can only be closed programmatically.
3428 cls String A custom CSS class to apply to the message box element
3429 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3430 displayed (defaults to 75)
3431 fn Function A callback function to execute after closing the dialog. The arguments to the
3432 function will be btn (the name of the button that was clicked, if applicable,
3433 e.g. "ok"), and text (the value of the active text field, if applicable).
3434 Progress and wait dialogs will ignore this option since they do not respond to
3435 user actions and can only be closed programmatically, so any required function
3436 should be called by the same code after it closes the dialog.
3437 icon String A CSS class that provides a background image to be used as an icon for
3438 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3439 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3440 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3441 modal Boolean False to allow user interaction with the page while the message box is
3442 displayed (defaults to true)
3443 msg String A string that will replace the existing message box body text (defaults
3444 to the XHTML-compliant non-breaking space character ' ')
3445 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3446 progress Boolean True to display a progress bar (defaults to false)
3447 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3448 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3449 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3450 title String The title text
3451 value String The string value to set into the active textbox element if displayed
3452 wait Boolean True to display a progress bar (defaults to false)
3453 width Number The width of the dialog in pixels
3460 msg: 'Please enter your address:',
3462 buttons: Roo.MessageBox.OKCANCEL,
3465 animEl: 'addAddressBtn'
3468 * @param {Object} config Configuration options
3469 * @return {Roo.MessageBox} This message box
3471 show : function(options)
3474 // this causes nightmares if you show one dialog after another
3475 // especially on callbacks..
3477 if(this.isVisible()){
3480 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3481 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3482 Roo.log("New Dialog Message:" + options.msg )
3483 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3484 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3487 var d = this.getDialog();
3489 d.setTitle(opt.title || " ");
3490 d.closeEl.setDisplayed(opt.closable !== false);
3491 activeTextEl = textboxEl;
3492 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3497 textareaEl.setHeight(typeof opt.multiline == "number" ?
3498 opt.multiline : this.defaultTextHeight);
3499 activeTextEl = textareaEl;
3508 progressEl.setDisplayed(opt.progress === true);
3509 this.updateProgress(0);
3510 activeTextEl.dom.value = opt.value || "";
3512 dlg.setDefaultButton(activeTextEl);
3514 var bs = opt.buttons;
3518 }else if(bs && bs.yes){
3519 db = buttons["yes"];
3521 dlg.setDefaultButton(db);
3523 bwidth = updateButtons(opt.buttons);
3524 this.updateText(opt.msg);
3526 d.el.addClass(opt.cls);
3528 d.proxyDrag = opt.proxyDrag === true;
3529 d.modal = opt.modal !== false;
3530 d.mask = opt.modal !== false ? mask : false;
3532 // force it to the end of the z-index stack so it gets a cursor in FF
3533 document.body.appendChild(dlg.el.dom);
3534 d.animateTarget = null;
3535 d.show(options.animEl);
3541 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3542 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3543 * and closing the message box when the process is complete.
3544 * @param {String} title The title bar text
3545 * @param {String} msg The message box body text
3546 * @return {Roo.MessageBox} This message box
3548 progress : function(title, msg){
3555 minWidth: this.minProgressWidth,
3562 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3563 * If a callback function is passed it will be called after the user clicks the button, and the
3564 * id of the button that was clicked will be passed as the only parameter to the callback
3565 * (could also be the top-right close button).
3566 * @param {String} title The title bar text
3567 * @param {String} msg The message box body text
3568 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3569 * @param {Object} scope (optional) The scope of the callback function
3570 * @return {Roo.MessageBox} This message box
3572 alert : function(title, msg, fn, scope)
3587 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3588 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3589 * You are responsible for closing the message box when the process is complete.
3590 * @param {String} msg The message box body text
3591 * @param {String} title (optional) The title bar text
3592 * @return {Roo.MessageBox} This message box
3594 wait : function(msg, title){
3605 waitTimer = Roo.TaskMgr.start({
3607 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3615 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3616 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3617 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3618 * @param {String} title The title bar text
3619 * @param {String} msg The message box body text
3620 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3621 * @param {Object} scope (optional) The scope of the callback function
3622 * @return {Roo.MessageBox} This message box
3624 confirm : function(title, msg, fn, scope){
3628 buttons: this.YESNO,
3637 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3638 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3639 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3640 * (could also be the top-right close button) and the text that was entered will be passed as the two
3641 * parameters to the callback.
3642 * @param {String} title The title bar text
3643 * @param {String} msg The message box body text
3644 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3645 * @param {Object} scope (optional) The scope of the callback function
3646 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3647 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3648 * @return {Roo.MessageBox} This message box
3650 prompt : function(title, msg, fn, scope, multiline){
3654 buttons: this.OKCANCEL,
3659 multiline: multiline,
3666 * Button config that displays a single OK button
3671 * Button config that displays Yes and No buttons
3674 YESNO : {yes:true, no:true},
3676 * Button config that displays OK and Cancel buttons
3679 OKCANCEL : {ok:true, cancel:true},
3681 * Button config that displays Yes, No and Cancel buttons
3684 YESNOCANCEL : {yes:true, no:true, cancel:true},
3687 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3690 defaultTextHeight : 75,
3692 * The maximum width in pixels of the message box (defaults to 600)
3697 * The minimum width in pixels of the message box (defaults to 100)
3702 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3703 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3706 minProgressWidth : 250,
3708 * An object containing the default button text strings that can be overriden for localized language support.
3709 * Supported properties are: ok, cancel, yes and no.
3710 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3723 * Shorthand for {@link Roo.MessageBox}
3725 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3726 Roo.Msg = Roo.Msg || Roo.MessageBox;
3735 * @class Roo.bootstrap.Navbar
3736 * @extends Roo.bootstrap.Component
3737 * Bootstrap Navbar class
3740 * Create a new Navbar
3741 * @param {Object} config The config object
3745 Roo.bootstrap.Navbar = function(config){
3746 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3750 * @event beforetoggle
3751 * Fire before toggle the menu
3752 * @param {Roo.EventObject} e
3754 "beforetoggle" : true
3758 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3767 getAutoCreate : function(){
3770 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3774 initEvents :function ()
3776 //Roo.log(this.el.select('.navbar-toggle',true));
3777 this.el.select('.navbar-toggle',true).on('click', function() {
3778 if(this.fireEvent('beforetoggle', this) !== false){
3779 this.el.select('.navbar-collapse',true).toggleClass('in');
3789 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3791 var size = this.el.getSize();
3792 this.maskEl.setSize(size.width, size.height);
3793 this.maskEl.enableDisplayMode("block");
3802 getChildContainer : function()
3804 if (this.el.select('.collapse').getCount()) {
3805 return this.el.select('.collapse',true).first();
3838 * @class Roo.bootstrap.NavSimplebar
3839 * @extends Roo.bootstrap.Navbar
3840 * Bootstrap Sidebar class
3842 * @cfg {Boolean} inverse is inverted color
3844 * @cfg {String} type (nav | pills | tabs)
3845 * @cfg {Boolean} arrangement stacked | justified
3846 * @cfg {String} align (left | right) alignment
3848 * @cfg {Boolean} main (true|false) main nav bar? default false
3849 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3851 * @cfg {String} tag (header|footer|nav|div) default is nav
3857 * Create a new Sidebar
3858 * @param {Object} config The config object
3862 Roo.bootstrap.NavSimplebar = function(config){
3863 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3866 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3882 getAutoCreate : function(){
3886 tag : this.tag || 'div',
3899 this.type = this.type || 'nav';
3900 if (['tabs','pills'].indexOf(this.type)!==-1) {
3901 cfg.cn[0].cls += ' nav-' + this.type
3905 if (this.type!=='nav') {
3906 Roo.log('nav type must be nav/tabs/pills')
3908 cfg.cn[0].cls += ' navbar-nav'
3914 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3915 cfg.cn[0].cls += ' nav-' + this.arrangement;
3919 if (this.align === 'right') {
3920 cfg.cn[0].cls += ' navbar-right';
3924 cfg.cls += ' navbar-inverse';
3948 * navbar-expand-md fixed-top
3952 * @class Roo.bootstrap.NavHeaderbar
3953 * @extends Roo.bootstrap.NavSimplebar
3954 * Bootstrap Sidebar class
3956 * @cfg {String} brand what is brand
3957 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3958 * @cfg {String} brand_href href of the brand
3959 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3960 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3961 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3962 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3965 * Create a new Sidebar
3966 * @param {Object} config The config object
3970 Roo.bootstrap.NavHeaderbar = function(config){
3971 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3975 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3982 desktopCenter : false,
3985 getAutoCreate : function(){
3988 tag: this.nav || 'nav',
3989 cls: 'navbar navbar-expand-md',
3995 if (this.desktopCenter) {
3996 cn.push({cls : 'container', cn : []});
4003 cls: 'navbar-header',
4008 cls: 'navbar-toggle navbar-toggler',
4009 'data-toggle': 'collapse',
4014 html: 'Toggle navigation'
4018 cls: 'icon-bar navbar-toggler-icon'
4036 cls: 'collapse navbar-collapse',
4040 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4042 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4043 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4045 // tag can override this..
4047 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4050 if (this.brand !== '') {
4053 href: this.brand_href ? this.brand_href : '#',
4054 cls: 'navbar-brand',
4062 cfg.cls += ' main-nav';
4070 getHeaderChildContainer : function()
4072 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4073 return this.el.select('.navbar-header',true).first();
4076 return this.getChildContainer();
4080 initEvents : function()
4082 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4084 if (this.autohide) {
4089 Roo.get(document).on('scroll',function(e) {
4090 var ns = Roo.get(document).getScroll().top;
4091 var os = prevScroll;
4095 ft.removeClass('slideDown');
4096 ft.addClass('slideUp');
4099 ft.removeClass('slideUp');
4100 ft.addClass('slideDown');
4121 * @class Roo.bootstrap.NavSidebar
4122 * @extends Roo.bootstrap.Navbar
4123 * Bootstrap Sidebar class
4126 * Create a new Sidebar
4127 * @param {Object} config The config object
4131 Roo.bootstrap.NavSidebar = function(config){
4132 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4135 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4137 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4139 getAutoCreate : function(){
4144 cls: 'sidebar sidebar-nav'
4166 * @class Roo.bootstrap.NavGroup
4167 * @extends Roo.bootstrap.Component
4168 * Bootstrap NavGroup class
4169 * @cfg {String} align (left|right)
4170 * @cfg {Boolean} inverse
4171 * @cfg {String} type (nav|pills|tab) default nav
4172 * @cfg {String} navId - reference Id for navbar.
4176 * Create a new nav group
4177 * @param {Object} config The config object
4180 Roo.bootstrap.NavGroup = function(config){
4181 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4184 Roo.bootstrap.NavGroup.register(this);
4188 * Fires when the active item changes
4189 * @param {Roo.bootstrap.NavGroup} this
4190 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4191 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4198 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4209 getAutoCreate : function()
4211 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4218 if (['tabs','pills'].indexOf(this.type)!==-1) {
4219 cfg.cls += ' nav-' + this.type
4221 if (this.type!=='nav') {
4222 Roo.log('nav type must be nav/tabs/pills')
4224 cfg.cls += ' navbar-nav mr-auto'
4227 if (this.parent() && this.parent().sidebar) {
4230 cls: 'dashboard-menu sidebar-menu'
4236 if (this.form === true) {
4242 if (this.align === 'right') {
4243 cfg.cls += ' navbar-right';
4245 cfg.cls += ' navbar-left';
4249 if (this.align === 'right') {
4250 cfg.cls += ' navbar-right';
4254 cfg.cls += ' navbar-inverse';
4262 * sets the active Navigation item
4263 * @param {Roo.bootstrap.NavItem} the new current navitem
4265 setActiveItem : function(item)
4268 Roo.each(this.navItems, function(v){
4273 v.setActive(false, true);
4280 item.setActive(true, true);
4281 this.fireEvent('changed', this, item, prev);
4286 * gets the active Navigation item
4287 * @return {Roo.bootstrap.NavItem} the current navitem
4289 getActive : function()
4293 Roo.each(this.navItems, function(v){
4304 indexOfNav : function()
4308 Roo.each(this.navItems, function(v,i){
4319 * adds a Navigation item
4320 * @param {Roo.bootstrap.NavItem} the navitem to add
4322 addItem : function(cfg)
4324 var cn = new Roo.bootstrap.NavItem(cfg);
4326 cn.parentId = this.id;
4327 cn.onRender(this.el, null);
4331 * register a Navigation item
4332 * @param {Roo.bootstrap.NavItem} the navitem to add
4334 register : function(item)
4336 this.navItems.push( item);
4337 item.navId = this.navId;
4342 * clear all the Navigation item
4345 clearAll : function()
4348 this.el.dom.innerHTML = '';
4351 getNavItem: function(tabId)
4354 Roo.each(this.navItems, function(e) {
4355 if (e.tabId == tabId) {
4365 setActiveNext : function()
4367 var i = this.indexOfNav(this.getActive());
4368 if (i > this.navItems.length) {
4371 this.setActiveItem(this.navItems[i+1]);
4373 setActivePrev : function()
4375 var i = this.indexOfNav(this.getActive());
4379 this.setActiveItem(this.navItems[i-1]);
4381 clearWasActive : function(except) {
4382 Roo.each(this.navItems, function(e) {
4383 if (e.tabId != except.tabId && e.was_active) {
4384 e.was_active = false;
4391 getWasActive : function ()
4394 Roo.each(this.navItems, function(e) {
4409 Roo.apply(Roo.bootstrap.NavGroup, {
4413 * register a Navigation Group
4414 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4416 register : function(navgrp)
4418 this.groups[navgrp.navId] = navgrp;
4422 * fetch a Navigation Group based on the navigation ID
4423 * @param {string} the navgroup to add
4424 * @returns {Roo.bootstrap.NavGroup} the navgroup
4426 get: function(navId) {
4427 if (typeof(this.groups[navId]) == 'undefined') {
4429 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4431 return this.groups[navId] ;
4446 * @class Roo.bootstrap.NavItem
4447 * @extends Roo.bootstrap.Component
4448 * Bootstrap Navbar.NavItem class
4449 * @cfg {String} href link to
4450 * @cfg {String} html content of button
4451 * @cfg {String} badge text inside badge
4452 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4453 * @cfg {String} glyphicon name of glyphicon
4454 * @cfg {String} icon name of font awesome icon
4455 * @cfg {Boolean} active Is item active
4456 * @cfg {Boolean} disabled Is item disabled
4458 * @cfg {Boolean} preventDefault (true | false) default false
4459 * @cfg {String} tabId the tab that this item activates.
4460 * @cfg {String} tagtype (a|span) render as a href or span?
4461 * @cfg {Boolean} animateRef (true|false) link to element default false
4464 * Create a new Navbar Item
4465 * @param {Object} config The config object
4467 Roo.bootstrap.NavItem = function(config){
4468 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4473 * The raw click event for the entire grid.
4474 * @param {Roo.EventObject} e
4479 * Fires when the active item active state changes
4480 * @param {Roo.bootstrap.NavItem} this
4481 * @param {boolean} state the new state
4487 * Fires when scroll to element
4488 * @param {Roo.bootstrap.NavItem} this
4489 * @param {Object} options
4490 * @param {Roo.EventObject} e
4498 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4506 preventDefault : false,
4513 getAutoCreate : function(){
4522 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4524 if (this.disabled) {
4525 cfg.cls += ' disabled';
4528 if (this.href || this.html || this.glyphicon || this.icon) {
4532 href : this.href || "#",
4533 html: this.html || ''
4536 if (this.tagtype == 'a') {
4537 cfg.cn[0].cls = 'nav-link';
4540 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4543 if(this.glyphicon) {
4544 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4549 cfg.cn[0].html += " <span class='caret'></span>";
4553 if (this.badge !== '') {
4555 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4563 initEvents: function()
4565 if (typeof (this.menu) != 'undefined') {
4566 this.menu.parentType = this.xtype;
4567 this.menu.triggerEl = this.el;
4568 this.menu = this.addxtype(Roo.apply({}, this.menu));
4571 this.el.select('a',true).on('click', this.onClick, this);
4573 if(this.tagtype == 'span'){
4574 this.el.select('span',true).on('click', this.onClick, this);
4577 // at this point parent should be available..
4578 this.parent().register(this);
4581 onClick : function(e)
4583 if (e.getTarget('.dropdown-menu-item')) {
4584 // did you click on a menu itemm.... - then don't trigger onclick..
4589 this.preventDefault ||
4592 Roo.log("NavItem - prevent Default?");
4596 if (this.disabled) {
4600 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4601 if (tg && tg.transition) {
4602 Roo.log("waiting for the transitionend");
4608 //Roo.log("fire event clicked");
4609 if(this.fireEvent('click', this, e) === false){
4613 if(this.tagtype == 'span'){
4617 //Roo.log(this.href);
4618 var ael = this.el.select('a',true).first();
4621 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4622 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4623 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4624 return; // ignore... - it's a 'hash' to another page.
4626 Roo.log("NavItem - prevent Default?");
4628 this.scrollToElement(e);
4632 var p = this.parent();
4634 if (['tabs','pills'].indexOf(p.type)!==-1) {
4635 if (typeof(p.setActiveItem) !== 'undefined') {
4636 p.setActiveItem(this);
4640 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4641 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4642 // remove the collapsed menu expand...
4643 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4647 isActive: function () {
4650 setActive : function(state, fire, is_was_active)
4652 if (this.active && !state && this.navId) {
4653 this.was_active = true;
4654 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4656 nv.clearWasActive(this);
4660 this.active = state;
4663 this.el.removeClass('active');
4664 } else if (!this.el.hasClass('active')) {
4665 this.el.addClass('active');
4668 this.fireEvent('changed', this, state);
4671 // show a panel if it's registered and related..
4673 if (!this.navId || !this.tabId || !state || is_was_active) {
4677 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4681 var pan = tg.getPanelByName(this.tabId);
4685 // if we can not flip to new panel - go back to old nav highlight..
4686 if (false == tg.showPanel(pan)) {
4687 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4689 var onav = nv.getWasActive();
4691 onav.setActive(true, false, true);
4700 // this should not be here...
4701 setDisabled : function(state)
4703 this.disabled = state;
4705 this.el.removeClass('disabled');
4706 } else if (!this.el.hasClass('disabled')) {
4707 this.el.addClass('disabled');
4713 * Fetch the element to display the tooltip on.
4714 * @return {Roo.Element} defaults to this.el
4716 tooltipEl : function()
4718 return this.el.select('' + this.tagtype + '', true).first();
4721 scrollToElement : function(e)
4723 var c = document.body;
4726 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4728 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4729 c = document.documentElement;
4732 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4738 var o = target.calcOffsetsTo(c);
4745 this.fireEvent('scrollto', this, options, e);
4747 Roo.get(c).scrollTo('top', options.value, true);
4760 * <span> icon </span>
4761 * <span> text </span>
4762 * <span>badge </span>
4766 * @class Roo.bootstrap.NavSidebarItem
4767 * @extends Roo.bootstrap.NavItem
4768 * Bootstrap Navbar.NavSidebarItem class
4769 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4770 * {Boolean} open is the menu open
4771 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4772 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4773 * {String} buttonSize (sm|md|lg)the extra classes for the button
4774 * {Boolean} showArrow show arrow next to the text (default true)
4776 * Create a new Navbar Button
4777 * @param {Object} config The config object
4779 Roo.bootstrap.NavSidebarItem = function(config){
4780 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4785 * The raw click event for the entire grid.
4786 * @param {Roo.EventObject} e
4791 * Fires when the active item active state changes
4792 * @param {Roo.bootstrap.NavSidebarItem} this
4793 * @param {boolean} state the new state
4801 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4803 badgeWeight : 'default',
4809 buttonWeight : 'default',
4815 getAutoCreate : function(){
4820 href : this.href || '#',
4826 if(this.buttonView){
4829 href : this.href || '#',
4830 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4843 cfg.cls += ' active';
4846 if (this.disabled) {
4847 cfg.cls += ' disabled';
4850 cfg.cls += ' open x-open';
4853 if (this.glyphicon || this.icon) {
4854 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4855 a.cn.push({ tag : 'i', cls : c }) ;
4858 if(!this.buttonView){
4861 html : this.html || ''
4868 if (this.badge !== '') {
4869 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4875 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4878 a.cls += ' dropdown-toggle treeview' ;
4884 initEvents : function()
4886 if (typeof (this.menu) != 'undefined') {
4887 this.menu.parentType = this.xtype;
4888 this.menu.triggerEl = this.el;
4889 this.menu = this.addxtype(Roo.apply({}, this.menu));
4892 this.el.on('click', this.onClick, this);
4894 if(this.badge !== ''){
4895 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4900 onClick : function(e)
4907 if(this.preventDefault){
4911 this.fireEvent('click', this);
4914 disable : function()
4916 this.setDisabled(true);
4921 this.setDisabled(false);
4924 setDisabled : function(state)
4926 if(this.disabled == state){
4930 this.disabled = state;
4933 this.el.addClass('disabled');
4937 this.el.removeClass('disabled');
4942 setActive : function(state)
4944 if(this.active == state){
4948 this.active = state;
4951 this.el.addClass('active');
4955 this.el.removeClass('active');
4960 isActive: function ()
4965 setBadge : function(str)
4971 this.badgeEl.dom.innerHTML = str;
4988 * @class Roo.bootstrap.Row
4989 * @extends Roo.bootstrap.Component
4990 * Bootstrap Row class (contains columns...)
4994 * @param {Object} config The config object
4997 Roo.bootstrap.Row = function(config){
4998 Roo.bootstrap.Row.superclass.constructor.call(this, config);
5001 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
5003 getAutoCreate : function(){
5022 * @class Roo.bootstrap.Element
5023 * @extends Roo.bootstrap.Component
5024 * Bootstrap Element class
5025 * @cfg {String} html contents of the element
5026 * @cfg {String} tag tag of the element
5027 * @cfg {String} cls class of the element
5028 * @cfg {Boolean} preventDefault (true|false) default false
5029 * @cfg {Boolean} clickable (true|false) default false
5032 * Create a new Element
5033 * @param {Object} config The config object
5036 Roo.bootstrap.Element = function(config){
5037 Roo.bootstrap.Element.superclass.constructor.call(this, config);
5043 * When a element is chick
5044 * @param {Roo.bootstrap.Element} this
5045 * @param {Roo.EventObject} e
5051 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
5056 preventDefault: false,
5059 getAutoCreate : function(){
5063 // cls: this.cls, double assign in parent class Component.js :: onRender
5070 initEvents: function()
5072 Roo.bootstrap.Element.superclass.initEvents.call(this);
5075 this.el.on('click', this.onClick, this);
5080 onClick : function(e)
5082 if(this.preventDefault){
5086 this.fireEvent('click', this, e);
5089 getValue : function()
5091 return this.el.dom.innerHTML;
5094 setValue : function(value)
5096 this.el.dom.innerHTML = value;
5111 * @class Roo.bootstrap.Pagination
5112 * @extends Roo.bootstrap.Component
5113 * Bootstrap Pagination class
5114 * @cfg {String} size xs | sm | md | lg
5115 * @cfg {Boolean} inverse false | true
5118 * Create a new Pagination
5119 * @param {Object} config The config object
5122 Roo.bootstrap.Pagination = function(config){
5123 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5126 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5132 getAutoCreate : function(){
5138 cfg.cls += ' inverse';
5144 cfg.cls += " " + this.cls;
5162 * @class Roo.bootstrap.PaginationItem
5163 * @extends Roo.bootstrap.Component
5164 * Bootstrap PaginationItem class
5165 * @cfg {String} html text
5166 * @cfg {String} href the link
5167 * @cfg {Boolean} preventDefault (true | false) default true
5168 * @cfg {Boolean} active (true | false) default false
5169 * @cfg {Boolean} disabled default false
5173 * Create a new PaginationItem
5174 * @param {Object} config The config object
5178 Roo.bootstrap.PaginationItem = function(config){
5179 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5184 * The raw click event for the entire grid.
5185 * @param {Roo.EventObject} e
5191 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5195 preventDefault: true,
5200 getAutoCreate : function(){
5206 href : this.href ? this.href : '#',
5207 html : this.html ? this.html : ''
5217 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5221 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5227 initEvents: function() {
5229 this.el.on('click', this.onClick, this);
5232 onClick : function(e)
5234 Roo.log('PaginationItem on click ');
5235 if(this.preventDefault){
5243 this.fireEvent('click', this, e);
5259 * @class Roo.bootstrap.Slider
5260 * @extends Roo.bootstrap.Component
5261 * Bootstrap Slider class
5264 * Create a new Slider
5265 * @param {Object} config The config object
5268 Roo.bootstrap.Slider = function(config){
5269 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5272 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5274 getAutoCreate : function(){
5278 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5282 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5294 * Ext JS Library 1.1.1
5295 * Copyright(c) 2006-2007, Ext JS, LLC.
5297 * Originally Released Under LGPL - original licence link has changed is not relivant.
5300 * <script type="text/javascript">
5305 * @class Roo.grid.ColumnModel
5306 * @extends Roo.util.Observable
5307 * This is the default implementation of a ColumnModel used by the Grid. It defines
5308 * the columns in the grid.
5311 var colModel = new Roo.grid.ColumnModel([
5312 {header: "Ticker", width: 60, sortable: true, locked: true},
5313 {header: "Company Name", width: 150, sortable: true},
5314 {header: "Market Cap.", width: 100, sortable: true},
5315 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5316 {header: "Employees", width: 100, sortable: true, resizable: false}
5321 * The config options listed for this class are options which may appear in each
5322 * individual column definition.
5323 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5325 * @param {Object} config An Array of column config objects. See this class's
5326 * config objects for details.
5328 Roo.grid.ColumnModel = function(config){
5330 * The config passed into the constructor
5332 this.config = config;
5335 // if no id, create one
5336 // if the column does not have a dataIndex mapping,
5337 // map it to the order it is in the config
5338 for(var i = 0, len = config.length; i < len; i++){
5340 if(typeof c.dataIndex == "undefined"){
5343 if(typeof c.renderer == "string"){
5344 c.renderer = Roo.util.Format[c.renderer];
5346 if(typeof c.id == "undefined"){
5349 if(c.editor && c.editor.xtype){
5350 c.editor = Roo.factory(c.editor, Roo.grid);
5352 if(c.editor && c.editor.isFormField){
5353 c.editor = new Roo.grid.GridEditor(c.editor);
5355 this.lookup[c.id] = c;
5359 * The width of columns which have no width specified (defaults to 100)
5362 this.defaultWidth = 100;
5365 * Default sortable of columns which have no sortable specified (defaults to false)
5368 this.defaultSortable = false;
5372 * @event widthchange
5373 * Fires when the width of a column changes.
5374 * @param {ColumnModel} this
5375 * @param {Number} columnIndex The column index
5376 * @param {Number} newWidth The new width
5378 "widthchange": true,
5380 * @event headerchange
5381 * Fires when the text of a header changes.
5382 * @param {ColumnModel} this
5383 * @param {Number} columnIndex The column index
5384 * @param {Number} newText The new header text
5386 "headerchange": true,
5388 * @event hiddenchange
5389 * Fires when a column is hidden or "unhidden".
5390 * @param {ColumnModel} this
5391 * @param {Number} columnIndex The column index
5392 * @param {Boolean} hidden true if hidden, false otherwise
5394 "hiddenchange": true,
5396 * @event columnmoved
5397 * Fires when a column is moved.
5398 * @param {ColumnModel} this
5399 * @param {Number} oldIndex
5400 * @param {Number} newIndex
5402 "columnmoved" : true,
5404 * @event columlockchange
5405 * Fires when a column's locked state is changed
5406 * @param {ColumnModel} this
5407 * @param {Number} colIndex
5408 * @param {Boolean} locked true if locked
5410 "columnlockchange" : true
5412 Roo.grid.ColumnModel.superclass.constructor.call(this);
5414 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5416 * @cfg {String} header The header text to display in the Grid view.
5419 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5420 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5421 * specified, the column's index is used as an index into the Record's data Array.
5424 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5425 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5428 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5429 * Defaults to the value of the {@link #defaultSortable} property.
5430 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5433 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5436 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5439 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5442 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5445 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5446 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5447 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5448 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5451 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5454 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5457 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5460 * @cfg {String} cursor (Optional)
5463 * @cfg {String} tooltip (Optional)
5466 * @cfg {Number} xs (Optional)
5469 * @cfg {Number} sm (Optional)
5472 * @cfg {Number} md (Optional)
5475 * @cfg {Number} lg (Optional)
5478 * Returns the id of the column at the specified index.
5479 * @param {Number} index The column index
5480 * @return {String} the id
5482 getColumnId : function(index){
5483 return this.config[index].id;
5487 * Returns the column for a specified id.
5488 * @param {String} id The column id
5489 * @return {Object} the column
5491 getColumnById : function(id){
5492 return this.lookup[id];
5497 * Returns the column for a specified dataIndex.
5498 * @param {String} dataIndex The column dataIndex
5499 * @return {Object|Boolean} the column or false if not found
5501 getColumnByDataIndex: function(dataIndex){
5502 var index = this.findColumnIndex(dataIndex);
5503 return index > -1 ? this.config[index] : false;
5507 * Returns the index for a specified column id.
5508 * @param {String} id The column id
5509 * @return {Number} the index, or -1 if not found
5511 getIndexById : function(id){
5512 for(var i = 0, len = this.config.length; i < len; i++){
5513 if(this.config[i].id == id){
5521 * Returns the index for a specified column dataIndex.
5522 * @param {String} dataIndex The column dataIndex
5523 * @return {Number} the index, or -1 if not found
5526 findColumnIndex : function(dataIndex){
5527 for(var i = 0, len = this.config.length; i < len; i++){
5528 if(this.config[i].dataIndex == dataIndex){
5536 moveColumn : function(oldIndex, newIndex){
5537 var c = this.config[oldIndex];
5538 this.config.splice(oldIndex, 1);
5539 this.config.splice(newIndex, 0, c);
5540 this.dataMap = null;
5541 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5544 isLocked : function(colIndex){
5545 return this.config[colIndex].locked === true;
5548 setLocked : function(colIndex, value, suppressEvent){
5549 if(this.isLocked(colIndex) == value){
5552 this.config[colIndex].locked = value;
5554 this.fireEvent("columnlockchange", this, colIndex, value);
5558 getTotalLockedWidth : function(){
5560 for(var i = 0; i < this.config.length; i++){
5561 if(this.isLocked(i) && !this.isHidden(i)){
5562 this.totalWidth += this.getColumnWidth(i);
5568 getLockedCount : function(){
5569 for(var i = 0, len = this.config.length; i < len; i++){
5570 if(!this.isLocked(i)){
5575 return this.config.length;
5579 * Returns the number of columns.
5582 getColumnCount : function(visibleOnly){
5583 if(visibleOnly === true){
5585 for(var i = 0, len = this.config.length; i < len; i++){
5586 if(!this.isHidden(i)){
5592 return this.config.length;
5596 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5597 * @param {Function} fn
5598 * @param {Object} scope (optional)
5599 * @return {Array} result
5601 getColumnsBy : function(fn, scope){
5603 for(var i = 0, len = this.config.length; i < len; i++){
5604 var c = this.config[i];
5605 if(fn.call(scope||this, c, i) === true){
5613 * Returns true if the specified column is sortable.
5614 * @param {Number} col The column index
5617 isSortable : function(col){
5618 if(typeof this.config[col].sortable == "undefined"){
5619 return this.defaultSortable;
5621 return this.config[col].sortable;
5625 * Returns the rendering (formatting) function defined for the column.
5626 * @param {Number} col The column index.
5627 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5629 getRenderer : function(col){
5630 if(!this.config[col].renderer){
5631 return Roo.grid.ColumnModel.defaultRenderer;
5633 return this.config[col].renderer;
5637 * Sets the rendering (formatting) function for a column.
5638 * @param {Number} col The column index
5639 * @param {Function} fn The function to use to process the cell's raw data
5640 * to return HTML markup for the grid view. The render function is called with
5641 * the following parameters:<ul>
5642 * <li>Data value.</li>
5643 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5644 * <li>css A CSS style string to apply to the table cell.</li>
5645 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5646 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5647 * <li>Row index</li>
5648 * <li>Column index</li>
5649 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5651 setRenderer : function(col, fn){
5652 this.config[col].renderer = fn;
5656 * Returns the width for the specified column.
5657 * @param {Number} col The column index
5660 getColumnWidth : function(col){
5661 return this.config[col].width * 1 || this.defaultWidth;
5665 * Sets the width for a column.
5666 * @param {Number} col The column index
5667 * @param {Number} width The new width
5669 setColumnWidth : function(col, width, suppressEvent){
5670 this.config[col].width = width;
5671 this.totalWidth = null;
5673 this.fireEvent("widthchange", this, col, width);
5678 * Returns the total width of all columns.
5679 * @param {Boolean} includeHidden True to include hidden column widths
5682 getTotalWidth : function(includeHidden){
5683 if(!this.totalWidth){
5684 this.totalWidth = 0;
5685 for(var i = 0, len = this.config.length; i < len; i++){
5686 if(includeHidden || !this.isHidden(i)){
5687 this.totalWidth += this.getColumnWidth(i);
5691 return this.totalWidth;
5695 * Returns the header for the specified column.
5696 * @param {Number} col The column index
5699 getColumnHeader : function(col){
5700 return this.config[col].header;
5704 * Sets the header for a column.
5705 * @param {Number} col The column index
5706 * @param {String} header The new header
5708 setColumnHeader : function(col, header){
5709 this.config[col].header = header;
5710 this.fireEvent("headerchange", this, col, header);
5714 * Returns the tooltip for the specified column.
5715 * @param {Number} col The column index
5718 getColumnTooltip : function(col){
5719 return this.config[col].tooltip;
5722 * Sets the tooltip for a column.
5723 * @param {Number} col The column index
5724 * @param {String} tooltip The new tooltip
5726 setColumnTooltip : function(col, tooltip){
5727 this.config[col].tooltip = tooltip;
5731 * Returns the dataIndex for the specified column.
5732 * @param {Number} col The column index
5735 getDataIndex : function(col){
5736 return this.config[col].dataIndex;
5740 * Sets the dataIndex for a column.
5741 * @param {Number} col The column index
5742 * @param {Number} dataIndex The new dataIndex
5744 setDataIndex : function(col, dataIndex){
5745 this.config[col].dataIndex = dataIndex;
5751 * Returns true if the cell is editable.
5752 * @param {Number} colIndex The column index
5753 * @param {Number} rowIndex The row index - this is nto actually used..?
5756 isCellEditable : function(colIndex, rowIndex){
5757 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5761 * Returns the editor defined for the cell/column.
5762 * return false or null to disable editing.
5763 * @param {Number} colIndex The column index
5764 * @param {Number} rowIndex The row index
5767 getCellEditor : function(colIndex, rowIndex){
5768 return this.config[colIndex].editor;
5772 * Sets if a column is editable.
5773 * @param {Number} col The column index
5774 * @param {Boolean} editable True if the column is editable
5776 setEditable : function(col, editable){
5777 this.config[col].editable = editable;
5782 * Returns true if the column is hidden.
5783 * @param {Number} colIndex The column index
5786 isHidden : function(colIndex){
5787 return this.config[colIndex].hidden;
5792 * Returns true if the column width cannot be changed
5794 isFixed : function(colIndex){
5795 return this.config[colIndex].fixed;
5799 * Returns true if the column can be resized
5802 isResizable : function(colIndex){
5803 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5806 * Sets if a column is hidden.
5807 * @param {Number} colIndex The column index
5808 * @param {Boolean} hidden True if the column is hidden
5810 setHidden : function(colIndex, hidden){
5811 this.config[colIndex].hidden = hidden;
5812 this.totalWidth = null;
5813 this.fireEvent("hiddenchange", this, colIndex, hidden);
5817 * Sets the editor for a column.
5818 * @param {Number} col The column index
5819 * @param {Object} editor The editor object
5821 setEditor : function(col, editor){
5822 this.config[col].editor = editor;
5826 Roo.grid.ColumnModel.defaultRenderer = function(value)
5828 if(typeof value == "object") {
5831 if(typeof value == "string" && value.length < 1){
5835 return String.format("{0}", value);
5838 // Alias for backwards compatibility
5839 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5842 * Ext JS Library 1.1.1
5843 * Copyright(c) 2006-2007, Ext JS, LLC.
5845 * Originally Released Under LGPL - original licence link has changed is not relivant.
5848 * <script type="text/javascript">
5852 * @class Roo.LoadMask
5853 * A simple utility class for generically masking elements while loading data. If the element being masked has
5854 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5855 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5856 * element's UpdateManager load indicator and will be destroyed after the initial load.
5858 * Create a new LoadMask
5859 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5860 * @param {Object} config The config object
5862 Roo.LoadMask = function(el, config){
5863 this.el = Roo.get(el);
5864 Roo.apply(this, config);
5866 this.store.on('beforeload', this.onBeforeLoad, this);
5867 this.store.on('load', this.onLoad, this);
5868 this.store.on('loadexception', this.onLoadException, this);
5869 this.removeMask = false;
5871 var um = this.el.getUpdateManager();
5872 um.showLoadIndicator = false; // disable the default indicator
5873 um.on('beforeupdate', this.onBeforeLoad, this);
5874 um.on('update', this.onLoad, this);
5875 um.on('failure', this.onLoad, this);
5876 this.removeMask = true;
5880 Roo.LoadMask.prototype = {
5882 * @cfg {Boolean} removeMask
5883 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5884 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5888 * The text to display in a centered loading message box (defaults to 'Loading...')
5892 * @cfg {String} msgCls
5893 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5895 msgCls : 'x-mask-loading',
5898 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5904 * Disables the mask to prevent it from being displayed
5906 disable : function(){
5907 this.disabled = true;
5911 * Enables the mask so that it can be displayed
5913 enable : function(){
5914 this.disabled = false;
5917 onLoadException : function()
5921 if (typeof(arguments[3]) != 'undefined') {
5922 Roo.MessageBox.alert("Error loading",arguments[3]);
5926 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5927 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5934 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5939 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5943 onBeforeLoad : function(){
5945 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5950 destroy : function(){
5952 this.store.un('beforeload', this.onBeforeLoad, this);
5953 this.store.un('load', this.onLoad, this);
5954 this.store.un('loadexception', this.onLoadException, this);
5956 var um = this.el.getUpdateManager();
5957 um.un('beforeupdate', this.onBeforeLoad, this);
5958 um.un('update', this.onLoad, this);
5959 um.un('failure', this.onLoad, this);
5970 * @class Roo.bootstrap.Table
5971 * @extends Roo.bootstrap.Component
5972 * Bootstrap Table class
5973 * @cfg {String} cls table class
5974 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5975 * @cfg {String} bgcolor Specifies the background color for a table
5976 * @cfg {Number} border Specifies whether the table cells should have borders or not
5977 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5978 * @cfg {Number} cellspacing Specifies the space between cells
5979 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5980 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5981 * @cfg {String} sortable Specifies that the table should be sortable
5982 * @cfg {String} summary Specifies a summary of the content of a table
5983 * @cfg {Number} width Specifies the width of a table
5984 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5986 * @cfg {boolean} striped Should the rows be alternative striped
5987 * @cfg {boolean} bordered Add borders to the table
5988 * @cfg {boolean} hover Add hover highlighting
5989 * @cfg {boolean} condensed Format condensed
5990 * @cfg {boolean} responsive Format condensed
5991 * @cfg {Boolean} loadMask (true|false) default false
5992 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5993 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5994 * @cfg {Boolean} rowSelection (true|false) default false
5995 * @cfg {Boolean} cellSelection (true|false) default false
5996 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5997 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5998 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
5999 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
6003 * Create a new Table
6004 * @param {Object} config The config object
6007 Roo.bootstrap.Table = function(config){
6008 Roo.bootstrap.Table.superclass.constructor.call(this, config);
6013 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6014 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6015 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6016 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6018 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6020 this.sm.grid = this;
6021 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6022 this.sm = this.selModel;
6023 this.sm.xmodule = this.xmodule || false;
6026 if (this.cm && typeof(this.cm.config) == 'undefined') {
6027 this.colModel = new Roo.grid.ColumnModel(this.cm);
6028 this.cm = this.colModel;
6029 this.cm.xmodule = this.xmodule || false;
6032 this.store= Roo.factory(this.store, Roo.data);
6033 this.ds = this.store;
6034 this.ds.xmodule = this.xmodule || false;
6037 if (this.footer && this.store) {
6038 this.footer.dataSource = this.ds;
6039 this.footer = Roo.factory(this.footer);
6046 * Fires when a cell is clicked
6047 * @param {Roo.bootstrap.Table} this
6048 * @param {Roo.Element} el
6049 * @param {Number} rowIndex
6050 * @param {Number} columnIndex
6051 * @param {Roo.EventObject} e
6055 * @event celldblclick
6056 * Fires when a cell is double clicked
6057 * @param {Roo.bootstrap.Table} this
6058 * @param {Roo.Element} el
6059 * @param {Number} rowIndex
6060 * @param {Number} columnIndex
6061 * @param {Roo.EventObject} e
6063 "celldblclick" : true,
6066 * Fires when a row is clicked
6067 * @param {Roo.bootstrap.Table} this
6068 * @param {Roo.Element} el
6069 * @param {Number} rowIndex
6070 * @param {Roo.EventObject} e
6074 * @event rowdblclick
6075 * Fires when a row is double clicked
6076 * @param {Roo.bootstrap.Table} this
6077 * @param {Roo.Element} el
6078 * @param {Number} rowIndex
6079 * @param {Roo.EventObject} e
6081 "rowdblclick" : true,
6084 * Fires when a mouseover occur
6085 * @param {Roo.bootstrap.Table} this
6086 * @param {Roo.Element} el
6087 * @param {Number} rowIndex
6088 * @param {Number} columnIndex
6089 * @param {Roo.EventObject} e
6094 * Fires when a mouseout occur
6095 * @param {Roo.bootstrap.Table} this
6096 * @param {Roo.Element} el
6097 * @param {Number} rowIndex
6098 * @param {Number} columnIndex
6099 * @param {Roo.EventObject} e
6104 * Fires when a row is rendered, so you can change add a style to it.
6105 * @param {Roo.bootstrap.Table} this
6106 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6110 * @event rowsrendered
6111 * Fires when all the rows have been rendered
6112 * @param {Roo.bootstrap.Table} this
6114 'rowsrendered' : true,
6116 * @event contextmenu
6117 * The raw contextmenu event for the entire grid.
6118 * @param {Roo.EventObject} e
6120 "contextmenu" : true,
6122 * @event rowcontextmenu
6123 * Fires when a row is right clicked
6124 * @param {Roo.bootstrap.Table} this
6125 * @param {Number} rowIndex
6126 * @param {Roo.EventObject} e
6128 "rowcontextmenu" : true,
6130 * @event cellcontextmenu
6131 * Fires when a cell is right clicked
6132 * @param {Roo.bootstrap.Table} this
6133 * @param {Number} rowIndex
6134 * @param {Number} cellIndex
6135 * @param {Roo.EventObject} e
6137 "cellcontextmenu" : true,
6139 * @event headercontextmenu
6140 * Fires when a header is right clicked
6141 * @param {Roo.bootstrap.Table} this
6142 * @param {Number} columnIndex
6143 * @param {Roo.EventObject} e
6145 "headercontextmenu" : true
6149 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6175 rowSelection : false,
6176 cellSelection : false,
6179 // Roo.Element - the tbody
6181 // Roo.Element - thead element
6184 container: false, // used by gridpanel...
6190 auto_hide_footer : false,
6192 getAutoCreate : function()
6194 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6201 if (this.scrollBody) {
6202 cfg.cls += ' table-body-fixed';
6205 cfg.cls += ' table-striped';
6209 cfg.cls += ' table-hover';
6211 if (this.bordered) {
6212 cfg.cls += ' table-bordered';
6214 if (this.condensed) {
6215 cfg.cls += ' table-condensed';
6217 if (this.responsive) {
6218 cfg.cls += ' table-responsive';
6222 cfg.cls+= ' ' +this.cls;
6225 // this lot should be simplifed...
6238 ].forEach(function(k) {
6246 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6249 if(this.store || this.cm){
6250 if(this.headerShow){
6251 cfg.cn.push(this.renderHeader());
6254 cfg.cn.push(this.renderBody());
6256 if(this.footerShow){
6257 cfg.cn.push(this.renderFooter());
6259 // where does this come from?
6260 //cfg.cls+= ' TableGrid';
6263 return { cn : [ cfg ] };
6266 initEvents : function()
6268 if(!this.store || !this.cm){
6271 if (this.selModel) {
6272 this.selModel.initEvents();
6276 //Roo.log('initEvents with ds!!!!');
6278 this.mainBody = this.el.select('tbody', true).first();
6279 this.mainHead = this.el.select('thead', true).first();
6280 this.mainFoot = this.el.select('tfoot', true).first();
6286 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6287 e.on('click', _this.sort, _this);
6290 this.mainBody.on("click", this.onClick, this);
6291 this.mainBody.on("dblclick", this.onDblClick, this);
6293 // why is this done????? = it breaks dialogs??
6294 //this.parent().el.setStyle('position', 'relative');
6298 this.footer.parentId = this.id;
6299 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6302 this.el.select('tfoot tr td').first().addClass('hide');
6307 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6310 this.store.on('load', this.onLoad, this);
6311 this.store.on('beforeload', this.onBeforeLoad, this);
6312 this.store.on('update', this.onUpdate, this);
6313 this.store.on('add', this.onAdd, this);
6314 this.store.on("clear", this.clear, this);
6316 this.el.on("contextmenu", this.onContextMenu, this);
6318 this.mainBody.on('scroll', this.onBodyScroll, this);
6320 this.cm.on("headerchange", this.onHeaderChange, this);
6322 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6326 onContextMenu : function(e, t)
6328 this.processEvent("contextmenu", e);
6331 processEvent : function(name, e)
6333 if (name != 'touchstart' ) {
6334 this.fireEvent(name, e);
6337 var t = e.getTarget();
6339 var cell = Roo.get(t);
6345 if(cell.findParent('tfoot', false, true)){
6349 if(cell.findParent('thead', false, true)){
6351 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6352 cell = Roo.get(t).findParent('th', false, true);
6354 Roo.log("failed to find th in thead?");
6355 Roo.log(e.getTarget());
6360 var cellIndex = cell.dom.cellIndex;
6362 var ename = name == 'touchstart' ? 'click' : name;
6363 this.fireEvent("header" + ename, this, cellIndex, e);
6368 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6369 cell = Roo.get(t).findParent('td', false, true);
6371 Roo.log("failed to find th in tbody?");
6372 Roo.log(e.getTarget());
6377 var row = cell.findParent('tr', false, true);
6378 var cellIndex = cell.dom.cellIndex;
6379 var rowIndex = row.dom.rowIndex - 1;
6383 this.fireEvent("row" + name, this, rowIndex, e);
6387 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6393 onMouseover : function(e, el)
6395 var cell = Roo.get(el);
6401 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6402 cell = cell.findParent('td', false, true);
6405 var row = cell.findParent('tr', false, true);
6406 var cellIndex = cell.dom.cellIndex;
6407 var rowIndex = row.dom.rowIndex - 1; // start from 0
6409 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6413 onMouseout : function(e, el)
6415 var cell = Roo.get(el);
6421 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6422 cell = cell.findParent('td', false, true);
6425 var row = cell.findParent('tr', false, true);
6426 var cellIndex = cell.dom.cellIndex;
6427 var rowIndex = row.dom.rowIndex - 1; // start from 0
6429 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6433 onClick : function(e, el)
6435 var cell = Roo.get(el);
6437 if(!cell || (!this.cellSelection && !this.rowSelection)){
6441 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6442 cell = cell.findParent('td', false, true);
6445 if(!cell || typeof(cell) == 'undefined'){
6449 var row = cell.findParent('tr', false, true);
6451 if(!row || typeof(row) == 'undefined'){
6455 var cellIndex = cell.dom.cellIndex;
6456 var rowIndex = this.getRowIndex(row);
6458 // why??? - should these not be based on SelectionModel?
6459 if(this.cellSelection){
6460 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6463 if(this.rowSelection){
6464 this.fireEvent('rowclick', this, row, rowIndex, e);
6470 onDblClick : function(e,el)
6472 var cell = Roo.get(el);
6474 if(!cell || (!this.cellSelection && !this.rowSelection)){
6478 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6479 cell = cell.findParent('td', false, true);
6482 if(!cell || typeof(cell) == 'undefined'){
6486 var row = cell.findParent('tr', false, true);
6488 if(!row || typeof(row) == 'undefined'){
6492 var cellIndex = cell.dom.cellIndex;
6493 var rowIndex = this.getRowIndex(row);
6495 if(this.cellSelection){
6496 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6499 if(this.rowSelection){
6500 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6504 sort : function(e,el)
6506 var col = Roo.get(el);
6508 if(!col.hasClass('sortable')){
6512 var sort = col.attr('sort');
6515 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6519 this.store.sortInfo = {field : sort, direction : dir};
6522 Roo.log("calling footer first");
6523 this.footer.onClick('first');
6526 this.store.load({ params : { start : 0 } });
6530 renderHeader : function()
6538 this.totalWidth = 0;
6540 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6542 var config = cm.config[i];
6546 cls : 'x-hcol-' + i,
6548 html: cm.getColumnHeader(i)
6553 if(typeof(config.sortable) != 'undefined' && config.sortable){
6555 c.html = '<i class="glyphicon"></i>' + c.html;
6558 if(typeof(config.lgHeader) != 'undefined'){
6559 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6562 if(typeof(config.mdHeader) != 'undefined'){
6563 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6566 if(typeof(config.smHeader) != 'undefined'){
6567 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6570 if(typeof(config.xsHeader) != 'undefined'){
6571 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6578 if(typeof(config.tooltip) != 'undefined'){
6579 c.tooltip = config.tooltip;
6582 if(typeof(config.colspan) != 'undefined'){
6583 c.colspan = config.colspan;
6586 if(typeof(config.hidden) != 'undefined' && config.hidden){
6587 c.style += ' display:none;';
6590 if(typeof(config.dataIndex) != 'undefined'){
6591 c.sort = config.dataIndex;
6596 if(typeof(config.align) != 'undefined' && config.align.length){
6597 c.style += ' text-align:' + config.align + ';';
6600 if(typeof(config.width) != 'undefined'){
6601 c.style += ' width:' + config.width + 'px;';
6602 this.totalWidth += config.width;
6604 this.totalWidth += 100; // assume minimum of 100 per column?
6607 if(typeof(config.cls) != 'undefined'){
6608 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6611 ['xs','sm','md','lg'].map(function(size){
6613 if(typeof(config[size]) == 'undefined'){
6617 if (!config[size]) { // 0 = hidden
6618 c.cls += ' hidden-' + size;
6622 c.cls += ' col-' + size + '-' + config[size];
6632 renderBody : function()
6642 colspan : this.cm.getColumnCount()
6652 renderFooter : function()
6662 colspan : this.cm.getColumnCount()
6676 // Roo.log('ds onload');
6681 var ds = this.store;
6683 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6684 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6685 if (_this.store.sortInfo) {
6687 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6688 e.select('i', true).addClass(['glyphicon-arrow-up']);
6691 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6692 e.select('i', true).addClass(['glyphicon-arrow-down']);
6697 var tbody = this.mainBody;
6699 if(ds.getCount() > 0){
6700 ds.data.each(function(d,rowIndex){
6701 var row = this.renderRow(cm, ds, rowIndex);
6703 tbody.createChild(row);
6707 if(row.cellObjects.length){
6708 Roo.each(row.cellObjects, function(r){
6709 _this.renderCellObject(r);
6716 var tfoot = this.el.select('tfoot', true).first();
6718 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6720 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6722 var total = this.ds.getTotalCount();
6724 if(this.footer.pageSize < total){
6725 this.mainFoot.show();
6729 Roo.each(this.el.select('tbody td', true).elements, function(e){
6730 e.on('mouseover', _this.onMouseover, _this);
6733 Roo.each(this.el.select('tbody td', true).elements, function(e){
6734 e.on('mouseout', _this.onMouseout, _this);
6736 this.fireEvent('rowsrendered', this);
6742 onUpdate : function(ds,record)
6744 this.refreshRow(record);
6748 onRemove : function(ds, record, index, isUpdate){
6749 if(isUpdate !== true){
6750 this.fireEvent("beforerowremoved", this, index, record);
6752 var bt = this.mainBody.dom;
6754 var rows = this.el.select('tbody > tr', true).elements;
6756 if(typeof(rows[index]) != 'undefined'){
6757 bt.removeChild(rows[index].dom);
6760 // if(bt.rows[index]){
6761 // bt.removeChild(bt.rows[index]);
6764 if(isUpdate !== true){
6765 //this.stripeRows(index);
6766 //this.syncRowHeights(index, index);
6768 this.fireEvent("rowremoved", this, index, record);
6772 onAdd : function(ds, records, rowIndex)
6774 //Roo.log('on Add called');
6775 // - note this does not handle multiple adding very well..
6776 var bt = this.mainBody.dom;
6777 for (var i =0 ; i < records.length;i++) {
6778 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6779 //Roo.log(records[i]);
6780 //Roo.log(this.store.getAt(rowIndex+i));
6781 this.insertRow(this.store, rowIndex + i, false);
6788 refreshRow : function(record){
6789 var ds = this.store, index;
6790 if(typeof record == 'number'){
6792 record = ds.getAt(index);
6794 index = ds.indexOf(record);
6796 this.insertRow(ds, index, true);
6798 this.onRemove(ds, record, index+1, true);
6800 //this.syncRowHeights(index, index);
6802 this.fireEvent("rowupdated", this, index, record);
6805 insertRow : function(dm, rowIndex, isUpdate){
6808 this.fireEvent("beforerowsinserted", this, rowIndex);
6810 //var s = this.getScrollState();
6811 var row = this.renderRow(this.cm, this.store, rowIndex);
6812 // insert before rowIndex..
6813 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6817 if(row.cellObjects.length){
6818 Roo.each(row.cellObjects, function(r){
6819 _this.renderCellObject(r);
6824 this.fireEvent("rowsinserted", this, rowIndex);
6825 //this.syncRowHeights(firstRow, lastRow);
6826 //this.stripeRows(firstRow);
6833 getRowDom : function(rowIndex)
6835 var rows = this.el.select('tbody > tr', true).elements;
6837 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6840 // returns the object tree for a tr..
6843 renderRow : function(cm, ds, rowIndex)
6845 var d = ds.getAt(rowIndex);
6849 cls : 'x-row-' + rowIndex,
6853 var cellObjects = [];
6855 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6856 var config = cm.config[i];
6858 var renderer = cm.getRenderer(i);
6862 if(typeof(renderer) !== 'undefined'){
6863 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6865 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6866 // and are rendered into the cells after the row is rendered - using the id for the element.
6868 if(typeof(value) === 'object'){
6878 rowIndex : rowIndex,
6883 this.fireEvent('rowclass', this, rowcfg);
6887 cls : rowcfg.rowClass + ' x-col-' + i,
6889 html: (typeof(value) === 'object') ? '' : value
6896 if(typeof(config.colspan) != 'undefined'){
6897 td.colspan = config.colspan;
6900 if(typeof(config.hidden) != 'undefined' && config.hidden){
6901 td.style += ' display:none;';
6904 if(typeof(config.align) != 'undefined' && config.align.length){
6905 td.style += ' text-align:' + config.align + ';';
6907 if(typeof(config.valign) != 'undefined' && config.valign.length){
6908 td.style += ' vertical-align:' + config.valign + ';';
6911 if(typeof(config.width) != 'undefined'){
6912 td.style += ' width:' + config.width + 'px;';
6915 if(typeof(config.cursor) != 'undefined'){
6916 td.style += ' cursor:' + config.cursor + ';';
6919 if(typeof(config.cls) != 'undefined'){
6920 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6923 ['xs','sm','md','lg'].map(function(size){
6925 if(typeof(config[size]) == 'undefined'){
6929 if (!config[size]) { // 0 = hidden
6930 td.cls += ' hidden-' + size;
6934 td.cls += ' col-' + size + '-' + config[size];
6942 row.cellObjects = cellObjects;
6950 onBeforeLoad : function()
6959 this.el.select('tbody', true).first().dom.innerHTML = '';
6962 * Show or hide a row.
6963 * @param {Number} rowIndex to show or hide
6964 * @param {Boolean} state hide
6966 setRowVisibility : function(rowIndex, state)
6968 var bt = this.mainBody.dom;
6970 var rows = this.el.select('tbody > tr', true).elements;
6972 if(typeof(rows[rowIndex]) == 'undefined'){
6975 rows[rowIndex].dom.style.display = state ? '' : 'none';
6979 getSelectionModel : function(){
6981 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6983 return this.selModel;
6986 * Render the Roo.bootstrap object from renderder
6988 renderCellObject : function(r)
6992 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6994 var t = r.cfg.render(r.container);
6997 Roo.each(r.cfg.cn, function(c){
6999 container: t.getChildContainer(),
7002 _this.renderCellObject(child);
7007 getRowIndex : function(row)
7011 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7022 * Returns the grid's underlying element = used by panel.Grid
7023 * @return {Element} The element
7025 getGridEl : function(){
7029 * Forces a resize - used by panel.Grid
7030 * @return {Element} The element
7032 autoSize : function()
7034 //var ctr = Roo.get(this.container.dom.parentElement);
7035 var ctr = Roo.get(this.el.dom);
7037 var thd = this.getGridEl().select('thead',true).first();
7038 var tbd = this.getGridEl().select('tbody', true).first();
7039 var tfd = this.getGridEl().select('tfoot', true).first();
7041 var cw = ctr.getWidth();
7045 tbd.setSize(ctr.getWidth(),
7046 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7048 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7051 cw = Math.max(cw, this.totalWidth);
7052 this.getGridEl().select('tr',true).setWidth(cw);
7053 // resize 'expandable coloumn?
7055 return; // we doe not have a view in this design..
7058 onBodyScroll: function()
7060 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7062 this.mainHead.setStyle({
7063 'position' : 'relative',
7064 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7070 var scrollHeight = this.mainBody.dom.scrollHeight;
7072 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7074 var height = this.mainBody.getHeight();
7076 if(scrollHeight - height == scrollTop) {
7078 var total = this.ds.getTotalCount();
7080 if(this.footer.cursor + this.footer.pageSize < total){
7082 this.footer.ds.load({
7084 start : this.footer.cursor + this.footer.pageSize,
7085 limit : this.footer.pageSize
7095 onHeaderChange : function()
7097 var header = this.renderHeader();
7098 var table = this.el.select('table', true).first();
7100 this.mainHead.remove();
7101 this.mainHead = table.createChild(header, this.mainBody, false);
7104 onHiddenChange : function(colModel, colIndex, hidden)
7106 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7107 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7109 this.CSS.updateRule(thSelector, "display", "");
7110 this.CSS.updateRule(tdSelector, "display", "");
7113 this.CSS.updateRule(thSelector, "display", "none");
7114 this.CSS.updateRule(tdSelector, "display", "none");
7117 this.onHeaderChange();
7121 setColumnWidth: function(col_index, width)
7123 // width = "md-2 xs-2..."
7124 if(!this.colModel.config[col_index]) {
7128 var w = width.split(" ");
7130 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7132 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7135 for(var j = 0; j < w.length; j++) {
7141 var size_cls = w[j].split("-");
7143 if(!Number.isInteger(size_cls[1] * 1)) {
7147 if(!this.colModel.config[col_index][size_cls[0]]) {
7151 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7155 h_row[0].classList.replace(
7156 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7157 "col-"+size_cls[0]+"-"+size_cls[1]
7160 for(var i = 0; i < rows.length; i++) {
7162 var size_cls = w[j].split("-");
7164 if(!Number.isInteger(size_cls[1] * 1)) {
7168 if(!this.colModel.config[col_index][size_cls[0]]) {
7172 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7176 rows[i].classList.replace(
7177 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7178 "col-"+size_cls[0]+"-"+size_cls[1]
7182 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7197 * @class Roo.bootstrap.TableCell
7198 * @extends Roo.bootstrap.Component
7199 * Bootstrap TableCell class
7200 * @cfg {String} html cell contain text
7201 * @cfg {String} cls cell class
7202 * @cfg {String} tag cell tag (td|th) default td
7203 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7204 * @cfg {String} align Aligns the content in a cell
7205 * @cfg {String} axis Categorizes cells
7206 * @cfg {String} bgcolor Specifies the background color of a cell
7207 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7208 * @cfg {Number} colspan Specifies the number of columns a cell should span
7209 * @cfg {String} headers Specifies one or more header cells a cell is related to
7210 * @cfg {Number} height Sets the height of a cell
7211 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7212 * @cfg {Number} rowspan Sets the number of rows a cell should span
7213 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7214 * @cfg {String} valign Vertical aligns the content in a cell
7215 * @cfg {Number} width Specifies the width of a cell
7218 * Create a new TableCell
7219 * @param {Object} config The config object
7222 Roo.bootstrap.TableCell = function(config){
7223 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7226 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7246 getAutoCreate : function(){
7247 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7267 cfg.align=this.align
7273 cfg.bgcolor=this.bgcolor
7276 cfg.charoff=this.charoff
7279 cfg.colspan=this.colspan
7282 cfg.headers=this.headers
7285 cfg.height=this.height
7288 cfg.nowrap=this.nowrap
7291 cfg.rowspan=this.rowspan
7294 cfg.scope=this.scope
7297 cfg.valign=this.valign
7300 cfg.width=this.width
7319 * @class Roo.bootstrap.TableRow
7320 * @extends Roo.bootstrap.Component
7321 * Bootstrap TableRow class
7322 * @cfg {String} cls row class
7323 * @cfg {String} align Aligns the content in a table row
7324 * @cfg {String} bgcolor Specifies a background color for a table row
7325 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7326 * @cfg {String} valign Vertical aligns the content in a table row
7329 * Create a new TableRow
7330 * @param {Object} config The config object
7333 Roo.bootstrap.TableRow = function(config){
7334 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7337 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7345 getAutoCreate : function(){
7346 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7356 cfg.align = this.align;
7359 cfg.bgcolor = this.bgcolor;
7362 cfg.charoff = this.charoff;
7365 cfg.valign = this.valign;
7383 * @class Roo.bootstrap.TableBody
7384 * @extends Roo.bootstrap.Component
7385 * Bootstrap TableBody class
7386 * @cfg {String} cls element class
7387 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7388 * @cfg {String} align Aligns the content inside the element
7389 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7390 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7393 * Create a new TableBody
7394 * @param {Object} config The config object
7397 Roo.bootstrap.TableBody = function(config){
7398 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7401 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7409 getAutoCreate : function(){
7410 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7424 cfg.align = this.align;
7427 cfg.charoff = this.charoff;
7430 cfg.valign = this.valign;
7437 // initEvents : function()
7444 // this.store = Roo.factory(this.store, Roo.data);
7445 // this.store.on('load', this.onLoad, this);
7447 // this.store.load();
7451 // onLoad: function ()
7453 // this.fireEvent('load', this);
7463 * Ext JS Library 1.1.1
7464 * Copyright(c) 2006-2007, Ext JS, LLC.
7466 * Originally Released Under LGPL - original licence link has changed is not relivant.
7469 * <script type="text/javascript">
7472 // as we use this in bootstrap.
7473 Roo.namespace('Roo.form');
7475 * @class Roo.form.Action
7476 * Internal Class used to handle form actions
7478 * @param {Roo.form.BasicForm} el The form element or its id
7479 * @param {Object} config Configuration options
7484 // define the action interface
7485 Roo.form.Action = function(form, options){
7487 this.options = options || {};
7490 * Client Validation Failed
7493 Roo.form.Action.CLIENT_INVALID = 'client';
7495 * Server Validation Failed
7498 Roo.form.Action.SERVER_INVALID = 'server';
7500 * Connect to Server Failed
7503 Roo.form.Action.CONNECT_FAILURE = 'connect';
7505 * Reading Data from Server Failed
7508 Roo.form.Action.LOAD_FAILURE = 'load';
7510 Roo.form.Action.prototype = {
7512 failureType : undefined,
7513 response : undefined,
7517 run : function(options){
7522 success : function(response){
7527 handleResponse : function(response){
7531 // default connection failure
7532 failure : function(response){
7534 this.response = response;
7535 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7536 this.form.afterAction(this, false);
7539 processResponse : function(response){
7540 this.response = response;
7541 if(!response.responseText){
7544 this.result = this.handleResponse(response);
7548 // utility functions used internally
7549 getUrl : function(appendParams){
7550 var url = this.options.url || this.form.url || this.form.el.dom.action;
7552 var p = this.getParams();
7554 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7560 getMethod : function(){
7561 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7564 getParams : function(){
7565 var bp = this.form.baseParams;
7566 var p = this.options.params;
7568 if(typeof p == "object"){
7569 p = Roo.urlEncode(Roo.applyIf(p, bp));
7570 }else if(typeof p == 'string' && bp){
7571 p += '&' + Roo.urlEncode(bp);
7574 p = Roo.urlEncode(bp);
7579 createCallback : function(){
7581 success: this.success,
7582 failure: this.failure,
7584 timeout: (this.form.timeout*1000),
7585 upload: this.form.fileUpload ? this.success : undefined
7590 Roo.form.Action.Submit = function(form, options){
7591 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7594 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7597 haveProgress : false,
7598 uploadComplete : false,
7600 // uploadProgress indicator.
7601 uploadProgress : function()
7603 if (!this.form.progressUrl) {
7607 if (!this.haveProgress) {
7608 Roo.MessageBox.progress("Uploading", "Uploading");
7610 if (this.uploadComplete) {
7611 Roo.MessageBox.hide();
7615 this.haveProgress = true;
7617 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7619 var c = new Roo.data.Connection();
7621 url : this.form.progressUrl,
7626 success : function(req){
7627 //console.log(data);
7631 rdata = Roo.decode(req.responseText)
7633 Roo.log("Invalid data from server..");
7637 if (!rdata || !rdata.success) {
7639 Roo.MessageBox.alert(Roo.encode(rdata));
7642 var data = rdata.data;
7644 if (this.uploadComplete) {
7645 Roo.MessageBox.hide();
7650 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7651 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7654 this.uploadProgress.defer(2000,this);
7657 failure: function(data) {
7658 Roo.log('progress url failed ');
7669 // run get Values on the form, so it syncs any secondary forms.
7670 this.form.getValues();
7672 var o = this.options;
7673 var method = this.getMethod();
7674 var isPost = method == 'POST';
7675 if(o.clientValidation === false || this.form.isValid()){
7677 if (this.form.progressUrl) {
7678 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7679 (new Date() * 1) + '' + Math.random());
7684 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7685 form:this.form.el.dom,
7686 url:this.getUrl(!isPost),
7688 params:isPost ? this.getParams() : null,
7689 isUpload: this.form.fileUpload
7692 this.uploadProgress();
7694 }else if (o.clientValidation !== false){ // client validation failed
7695 this.failureType = Roo.form.Action.CLIENT_INVALID;
7696 this.form.afterAction(this, false);
7700 success : function(response)
7702 this.uploadComplete= true;
7703 if (this.haveProgress) {
7704 Roo.MessageBox.hide();
7708 var result = this.processResponse(response);
7709 if(result === true || result.success){
7710 this.form.afterAction(this, true);
7714 this.form.markInvalid(result.errors);
7715 this.failureType = Roo.form.Action.SERVER_INVALID;
7717 this.form.afterAction(this, false);
7719 failure : function(response)
7721 this.uploadComplete= true;
7722 if (this.haveProgress) {
7723 Roo.MessageBox.hide();
7726 this.response = response;
7727 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7728 this.form.afterAction(this, false);
7731 handleResponse : function(response){
7732 if(this.form.errorReader){
7733 var rs = this.form.errorReader.read(response);
7736 for(var i = 0, len = rs.records.length; i < len; i++) {
7737 var r = rs.records[i];
7741 if(errors.length < 1){
7745 success : rs.success,
7751 ret = Roo.decode(response.responseText);
7755 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7765 Roo.form.Action.Load = function(form, options){
7766 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7767 this.reader = this.form.reader;
7770 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7775 Roo.Ajax.request(Roo.apply(
7776 this.createCallback(), {
7777 method:this.getMethod(),
7778 url:this.getUrl(false),
7779 params:this.getParams()
7783 success : function(response){
7785 var result = this.processResponse(response);
7786 if(result === true || !result.success || !result.data){
7787 this.failureType = Roo.form.Action.LOAD_FAILURE;
7788 this.form.afterAction(this, false);
7791 this.form.clearInvalid();
7792 this.form.setValues(result.data);
7793 this.form.afterAction(this, true);
7796 handleResponse : function(response){
7797 if(this.form.reader){
7798 var rs = this.form.reader.read(response);
7799 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7801 success : rs.success,
7805 return Roo.decode(response.responseText);
7809 Roo.form.Action.ACTION_TYPES = {
7810 'load' : Roo.form.Action.Load,
7811 'submit' : Roo.form.Action.Submit
7820 * @class Roo.bootstrap.Form
7821 * @extends Roo.bootstrap.Component
7822 * Bootstrap Form class
7823 * @cfg {String} method GET | POST (default POST)
7824 * @cfg {String} labelAlign top | left (default top)
7825 * @cfg {String} align left | right - for navbars
7826 * @cfg {Boolean} loadMask load mask when submit (default true)
7831 * @param {Object} config The config object
7835 Roo.bootstrap.Form = function(config){
7837 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7839 Roo.bootstrap.Form.popover.apply();
7843 * @event clientvalidation
7844 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7845 * @param {Form} this
7846 * @param {Boolean} valid true if the form has passed client-side validation
7848 clientvalidation: true,
7850 * @event beforeaction
7851 * Fires before any action is performed. Return false to cancel the action.
7852 * @param {Form} this
7853 * @param {Action} action The action to be performed
7857 * @event actionfailed
7858 * Fires when an action fails.
7859 * @param {Form} this
7860 * @param {Action} action The action that failed
7862 actionfailed : true,
7864 * @event actioncomplete
7865 * Fires when an action is completed.
7866 * @param {Form} this
7867 * @param {Action} action The action that completed
7869 actioncomplete : true
7873 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7876 * @cfg {String} method
7877 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7882 * The URL to use for form actions if one isn't supplied in the action options.
7885 * @cfg {Boolean} fileUpload
7886 * Set to true if this form is a file upload.
7890 * @cfg {Object} baseParams
7891 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7895 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7899 * @cfg {Sting} align (left|right) for navbar forms
7904 activeAction : null,
7907 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7908 * element by passing it or its id or mask the form itself by passing in true.
7911 waitMsgTarget : false,
7916 * @cfg {Boolean} errorMask (true|false) default false
7921 * @cfg {Number} maskOffset Default 100
7926 * @cfg {Boolean} maskBody
7930 getAutoCreate : function(){
7934 method : this.method || 'POST',
7935 id : this.id || Roo.id(),
7938 if (this.parent().xtype.match(/^Nav/)) {
7939 cfg.cls = 'navbar-form navbar-' + this.align;
7943 if (this.labelAlign == 'left' ) {
7944 cfg.cls += ' form-horizontal';
7950 initEvents : function()
7952 this.el.on('submit', this.onSubmit, this);
7953 // this was added as random key presses on the form where triggering form submit.
7954 this.el.on('keypress', function(e) {
7955 if (e.getCharCode() != 13) {
7958 // we might need to allow it for textareas.. and some other items.
7959 // check e.getTarget().
7961 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7965 Roo.log("keypress blocked");
7973 onSubmit : function(e){
7978 * Returns true if client-side validation on the form is successful.
7981 isValid : function(){
7982 var items = this.getItems();
7986 items.each(function(f){
7992 Roo.log('invalid field: ' + f.name);
7996 if(!target && f.el.isVisible(true)){
8002 if(this.errorMask && !valid){
8003 Roo.bootstrap.Form.popover.mask(this, target);
8010 * Returns true if any fields in this form have changed since their original load.
8013 isDirty : function(){
8015 var items = this.getItems();
8016 items.each(function(f){
8026 * Performs a predefined action (submit or load) or custom actions you define on this form.
8027 * @param {String} actionName The name of the action type
8028 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
8029 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8030 * accept other config options):
8032 Property Type Description
8033 ---------------- --------------- ----------------------------------------------------------------------------------
8034 url String The url for the action (defaults to the form's url)
8035 method String The form method to use (defaults to the form's method, or POST if not defined)
8036 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
8037 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
8038 validate the form on the client (defaults to false)
8040 * @return {BasicForm} this
8042 doAction : function(action, options){
8043 if(typeof action == 'string'){
8044 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8046 if(this.fireEvent('beforeaction', this, action) !== false){
8047 this.beforeAction(action);
8048 action.run.defer(100, action);
8054 beforeAction : function(action){
8055 var o = action.options;
8060 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8062 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8065 // not really supported yet.. ??
8067 //if(this.waitMsgTarget === true){
8068 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8069 //}else if(this.waitMsgTarget){
8070 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8071 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8073 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8079 afterAction : function(action, success){
8080 this.activeAction = null;
8081 var o = action.options;
8086 Roo.get(document.body).unmask();
8092 //if(this.waitMsgTarget === true){
8093 // this.el.unmask();
8094 //}else if(this.waitMsgTarget){
8095 // this.waitMsgTarget.unmask();
8097 // Roo.MessageBox.updateProgress(1);
8098 // Roo.MessageBox.hide();
8105 Roo.callback(o.success, o.scope, [this, action]);
8106 this.fireEvent('actioncomplete', this, action);
8110 // failure condition..
8111 // we have a scenario where updates need confirming.
8112 // eg. if a locking scenario exists..
8113 // we look for { errors : { needs_confirm : true }} in the response.
8115 (typeof(action.result) != 'undefined') &&
8116 (typeof(action.result.errors) != 'undefined') &&
8117 (typeof(action.result.errors.needs_confirm) != 'undefined')
8120 Roo.log("not supported yet");
8123 Roo.MessageBox.confirm(
8124 "Change requires confirmation",
8125 action.result.errorMsg,
8130 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8140 Roo.callback(o.failure, o.scope, [this, action]);
8141 // show an error message if no failed handler is set..
8142 if (!this.hasListener('actionfailed')) {
8143 Roo.log("need to add dialog support");
8145 Roo.MessageBox.alert("Error",
8146 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8147 action.result.errorMsg :
8148 "Saving Failed, please check your entries or try again"
8153 this.fireEvent('actionfailed', this, action);
8158 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8159 * @param {String} id The value to search for
8162 findField : function(id){
8163 var items = this.getItems();
8164 var field = items.get(id);
8166 items.each(function(f){
8167 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8174 return field || null;
8177 * Mark fields in this form invalid in bulk.
8178 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8179 * @return {BasicForm} this
8181 markInvalid : function(errors){
8182 if(errors instanceof Array){
8183 for(var i = 0, len = errors.length; i < len; i++){
8184 var fieldError = errors[i];
8185 var f = this.findField(fieldError.id);
8187 f.markInvalid(fieldError.msg);
8193 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8194 field.markInvalid(errors[id]);
8198 //Roo.each(this.childForms || [], function (f) {
8199 // f.markInvalid(errors);
8206 * Set values for fields in this form in bulk.
8207 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8208 * @return {BasicForm} this
8210 setValues : function(values){
8211 if(values instanceof Array){ // array of objects
8212 for(var i = 0, len = values.length; i < len; i++){
8214 var f = this.findField(v.id);
8216 f.setValue(v.value);
8217 if(this.trackResetOnLoad){
8218 f.originalValue = f.getValue();
8222 }else{ // object hash
8225 if(typeof values[id] != 'function' && (field = this.findField(id))){
8227 if (field.setFromData &&
8229 field.displayField &&
8230 // combos' with local stores can
8231 // be queried via setValue()
8232 // to set their value..
8233 (field.store && !field.store.isLocal)
8237 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8238 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8239 field.setFromData(sd);
8241 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8243 field.setFromData(values);
8246 field.setValue(values[id]);
8250 if(this.trackResetOnLoad){
8251 field.originalValue = field.getValue();
8257 //Roo.each(this.childForms || [], function (f) {
8258 // f.setValues(values);
8265 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8266 * they are returned as an array.
8267 * @param {Boolean} asString
8270 getValues : function(asString){
8271 //if (this.childForms) {
8272 // copy values from the child forms
8273 // Roo.each(this.childForms, function (f) {
8274 // this.setValues(f.getValues());
8280 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8281 if(asString === true){
8284 return Roo.urlDecode(fs);
8288 * Returns the fields in this form as an object with key/value pairs.
8289 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8292 getFieldValues : function(with_hidden)
8294 var items = this.getItems();
8296 items.each(function(f){
8302 var v = f.getValue();
8304 if (f.inputType =='radio') {
8305 if (typeof(ret[f.getName()]) == 'undefined') {
8306 ret[f.getName()] = ''; // empty..
8309 if (!f.el.dom.checked) {
8317 if(f.xtype == 'MoneyField'){
8318 ret[f.currencyName] = f.getCurrency();
8321 // not sure if this supported any more..
8322 if ((typeof(v) == 'object') && f.getRawValue) {
8323 v = f.getRawValue() ; // dates..
8325 // combo boxes where name != hiddenName...
8326 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8327 ret[f.name] = f.getRawValue();
8329 ret[f.getName()] = v;
8336 * Clears all invalid messages in this form.
8337 * @return {BasicForm} this
8339 clearInvalid : function(){
8340 var items = this.getItems();
8342 items.each(function(f){
8351 * @return {BasicForm} this
8354 var items = this.getItems();
8355 items.each(function(f){
8359 Roo.each(this.childForms || [], function (f) {
8367 getItems : function()
8369 var r=new Roo.util.MixedCollection(false, function(o){
8370 return o.id || (o.id = Roo.id());
8372 var iter = function(el) {
8379 Roo.each(el.items,function(e) {
8388 hideFields : function(items)
8390 Roo.each(items, function(i){
8392 var f = this.findField(i);
8403 showFields : function(items)
8405 Roo.each(items, function(i){
8407 var f = this.findField(i);
8420 Roo.apply(Roo.bootstrap.Form, {
8447 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8448 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8449 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8450 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8453 this.maskEl.top.enableDisplayMode("block");
8454 this.maskEl.left.enableDisplayMode("block");
8455 this.maskEl.bottom.enableDisplayMode("block");
8456 this.maskEl.right.enableDisplayMode("block");
8458 this.toolTip = new Roo.bootstrap.Tooltip({
8459 cls : 'roo-form-error-popover',
8461 'left' : ['r-l', [-2,0], 'right'],
8462 'right' : ['l-r', [2,0], 'left'],
8463 'bottom' : ['tl-bl', [0,2], 'top'],
8464 'top' : [ 'bl-tl', [0,-2], 'bottom']
8468 this.toolTip.render(Roo.get(document.body));
8470 this.toolTip.el.enableDisplayMode("block");
8472 Roo.get(document.body).on('click', function(){
8476 Roo.get(document.body).on('touchstart', function(){
8480 this.isApplied = true
8483 mask : function(form, target)
8487 this.target = target;
8489 if(!this.form.errorMask || !target.el){
8493 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8495 Roo.log(scrollable);
8497 var ot = this.target.el.calcOffsetsTo(scrollable);
8499 var scrollTo = ot[1] - this.form.maskOffset;
8501 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8503 scrollable.scrollTo('top', scrollTo);
8505 var box = this.target.el.getBox();
8507 var zIndex = Roo.bootstrap.Modal.zIndex++;
8510 this.maskEl.top.setStyle('position', 'absolute');
8511 this.maskEl.top.setStyle('z-index', zIndex);
8512 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8513 this.maskEl.top.setLeft(0);
8514 this.maskEl.top.setTop(0);
8515 this.maskEl.top.show();
8517 this.maskEl.left.setStyle('position', 'absolute');
8518 this.maskEl.left.setStyle('z-index', zIndex);
8519 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8520 this.maskEl.left.setLeft(0);
8521 this.maskEl.left.setTop(box.y - this.padding);
8522 this.maskEl.left.show();
8524 this.maskEl.bottom.setStyle('position', 'absolute');
8525 this.maskEl.bottom.setStyle('z-index', zIndex);
8526 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8527 this.maskEl.bottom.setLeft(0);
8528 this.maskEl.bottom.setTop(box.bottom + this.padding);
8529 this.maskEl.bottom.show();
8531 this.maskEl.right.setStyle('position', 'absolute');
8532 this.maskEl.right.setStyle('z-index', zIndex);
8533 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8534 this.maskEl.right.setLeft(box.right + this.padding);
8535 this.maskEl.right.setTop(box.y - this.padding);
8536 this.maskEl.right.show();
8538 this.toolTip.bindEl = this.target.el;
8540 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8542 var tip = this.target.blankText;
8544 if(this.target.getValue() !== '' ) {
8546 if (this.target.invalidText.length) {
8547 tip = this.target.invalidText;
8548 } else if (this.target.regexText.length){
8549 tip = this.target.regexText;
8553 this.toolTip.show(tip);
8555 this.intervalID = window.setInterval(function() {
8556 Roo.bootstrap.Form.popover.unmask();
8559 window.onwheel = function(){ return false;};
8561 (function(){ this.isMasked = true; }).defer(500, this);
8567 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8571 this.maskEl.top.setStyle('position', 'absolute');
8572 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8573 this.maskEl.top.hide();
8575 this.maskEl.left.setStyle('position', 'absolute');
8576 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8577 this.maskEl.left.hide();
8579 this.maskEl.bottom.setStyle('position', 'absolute');
8580 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8581 this.maskEl.bottom.hide();
8583 this.maskEl.right.setStyle('position', 'absolute');
8584 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8585 this.maskEl.right.hide();
8587 this.toolTip.hide();
8589 this.toolTip.el.hide();
8591 window.onwheel = function(){ return true;};
8593 if(this.intervalID){
8594 window.clearInterval(this.intervalID);
8595 this.intervalID = false;
8598 this.isMasked = false;
8608 * Ext JS Library 1.1.1
8609 * Copyright(c) 2006-2007, Ext JS, LLC.
8611 * Originally Released Under LGPL - original licence link has changed is not relivant.
8614 * <script type="text/javascript">
8617 * @class Roo.form.VTypes
8618 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8621 Roo.form.VTypes = function(){
8622 // closure these in so they are only created once.
8623 var alpha = /^[a-zA-Z_]+$/;
8624 var alphanum = /^[a-zA-Z0-9_]+$/;
8625 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8626 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8628 // All these messages and functions are configurable
8631 * The function used to validate email addresses
8632 * @param {String} value The email address
8634 'email' : function(v){
8635 return email.test(v);
8638 * The error text to display when the email validation function returns false
8641 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8643 * The keystroke filter mask to be applied on email input
8646 'emailMask' : /[a-z0-9_\.\-@]/i,
8649 * The function used to validate URLs
8650 * @param {String} value The URL
8652 'url' : function(v){
8656 * The error text to display when the url validation function returns false
8659 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8662 * The function used to validate alpha values
8663 * @param {String} value The value
8665 'alpha' : function(v){
8666 return alpha.test(v);
8669 * The error text to display when the alpha validation function returns false
8672 'alphaText' : 'This field should only contain letters and _',
8674 * The keystroke filter mask to be applied on alpha input
8677 'alphaMask' : /[a-z_]/i,
8680 * The function used to validate alphanumeric values
8681 * @param {String} value The value
8683 'alphanum' : function(v){
8684 return alphanum.test(v);
8687 * The error text to display when the alphanumeric validation function returns false
8690 'alphanumText' : 'This field should only contain letters, numbers and _',
8692 * The keystroke filter mask to be applied on alphanumeric input
8695 'alphanumMask' : /[a-z0-9_]/i
8705 * @class Roo.bootstrap.Input
8706 * @extends Roo.bootstrap.Component
8707 * Bootstrap Input class
8708 * @cfg {Boolean} disabled is it disabled
8709 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8710 * @cfg {String} name name of the input
8711 * @cfg {string} fieldLabel - the label associated
8712 * @cfg {string} placeholder - placeholder to put in text.
8713 * @cfg {string} before - input group add on before
8714 * @cfg {string} after - input group add on after
8715 * @cfg {string} size - (lg|sm) or leave empty..
8716 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8717 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8718 * @cfg {Number} md colspan out of 12 for computer-sized screens
8719 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8720 * @cfg {string} value default value of the input
8721 * @cfg {Number} labelWidth set the width of label
8722 * @cfg {Number} labellg set the width of label (1-12)
8723 * @cfg {Number} labelmd set the width of label (1-12)
8724 * @cfg {Number} labelsm set the width of label (1-12)
8725 * @cfg {Number} labelxs set the width of label (1-12)
8726 * @cfg {String} labelAlign (top|left)
8727 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8728 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8729 * @cfg {String} indicatorpos (left|right) default left
8730 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8731 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8733 * @cfg {String} align (left|center|right) Default left
8734 * @cfg {Boolean} forceFeedback (true|false) Default false
8737 * Create a new Input
8738 * @param {Object} config The config object
8741 Roo.bootstrap.Input = function(config){
8743 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8748 * Fires when this field receives input focus.
8749 * @param {Roo.form.Field} this
8754 * Fires when this field loses input focus.
8755 * @param {Roo.form.Field} this
8760 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8761 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8762 * @param {Roo.form.Field} this
8763 * @param {Roo.EventObject} e The event object
8768 * Fires just before the field blurs if the field value has changed.
8769 * @param {Roo.form.Field} this
8770 * @param {Mixed} newValue The new value
8771 * @param {Mixed} oldValue The original value
8776 * Fires after the field has been marked as invalid.
8777 * @param {Roo.form.Field} this
8778 * @param {String} msg The validation message
8783 * Fires after the field has been validated with no errors.
8784 * @param {Roo.form.Field} this
8789 * Fires after the key up
8790 * @param {Roo.form.Field} this
8791 * @param {Roo.EventObject} e The event Object
8797 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8799 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8800 automatic validation (defaults to "keyup").
8802 validationEvent : "keyup",
8804 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8806 validateOnBlur : true,
8808 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8810 validationDelay : 250,
8812 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8814 focusClass : "x-form-focus", // not needed???
8818 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8820 invalidClass : "has-warning",
8823 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8825 validClass : "has-success",
8828 * @cfg {Boolean} hasFeedback (true|false) default true
8833 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8835 invalidFeedbackClass : "glyphicon-warning-sign",
8838 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8840 validFeedbackClass : "glyphicon-ok",
8843 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8845 selectOnFocus : false,
8848 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8852 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8857 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8859 disableKeyFilter : false,
8862 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8866 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8870 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8872 blankText : "Please complete this mandatory field",
8875 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8879 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8881 maxLength : Number.MAX_VALUE,
8883 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8885 minLengthText : "The minimum length for this field is {0}",
8887 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8889 maxLengthText : "The maximum length for this field is {0}",
8893 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8894 * If available, this function will be called only after the basic validators all return true, and will be passed the
8895 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8899 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8900 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8901 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8905 * @cfg {String} regexText -- Depricated - use Invalid Text
8910 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8916 autocomplete: false,
8935 formatedValue : false,
8936 forceFeedback : false,
8938 indicatorpos : 'left',
8948 parentLabelAlign : function()
8951 while (parent.parent()) {
8952 parent = parent.parent();
8953 if (typeof(parent.labelAlign) !='undefined') {
8954 return parent.labelAlign;
8961 getAutoCreate : function()
8963 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8969 if(this.inputType != 'hidden'){
8970 cfg.cls = 'form-group' //input-group
8976 type : this.inputType,
8978 cls : 'form-control',
8979 placeholder : this.placeholder || '',
8980 autocomplete : this.autocomplete || 'new-password'
8983 if(this.capture.length){
8984 input.capture = this.capture;
8987 if(this.accept.length){
8988 input.accept = this.accept + "/*";
8992 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8995 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8996 input.maxLength = this.maxLength;
8999 if (this.disabled) {
9000 input.disabled=true;
9003 if (this.readOnly) {
9004 input.readonly=true;
9008 input.name = this.name;
9012 input.cls += ' input-' + this.size;
9016 ['xs','sm','md','lg'].map(function(size){
9017 if (settings[size]) {
9018 cfg.cls += ' col-' + size + '-' + settings[size];
9022 var inputblock = input;
9026 cls: 'glyphicon form-control-feedback'
9029 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9032 cls : 'has-feedback',
9040 if (this.before || this.after) {
9043 cls : 'input-group',
9047 if (this.before && typeof(this.before) == 'string') {
9049 inputblock.cn.push({
9051 cls : 'roo-input-before input-group-addon',
9055 if (this.before && typeof(this.before) == 'object') {
9056 this.before = Roo.factory(this.before);
9058 inputblock.cn.push({
9060 cls : 'roo-input-before input-group-' +
9061 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9065 inputblock.cn.push(input);
9067 if (this.after && typeof(this.after) == 'string') {
9068 inputblock.cn.push({
9070 cls : 'roo-input-after input-group-addon',
9074 if (this.after && typeof(this.after) == 'object') {
9075 this.after = Roo.factory(this.after);
9077 inputblock.cn.push({
9079 cls : 'roo-input-after input-group-' +
9080 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9084 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9085 inputblock.cls += ' has-feedback';
9086 inputblock.cn.push(feedback);
9090 if (align ==='left' && this.fieldLabel.length) {
9092 cfg.cls += ' roo-form-group-label-left';
9097 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9098 tooltip : 'This field is required'
9103 cls : 'control-label',
9104 html : this.fieldLabel
9115 var labelCfg = cfg.cn[1];
9116 var contentCfg = cfg.cn[2];
9118 if(this.indicatorpos == 'right'){
9123 cls : 'control-label',
9127 html : this.fieldLabel
9131 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9132 tooltip : 'This field is required'
9145 labelCfg = cfg.cn[0];
9146 contentCfg = cfg.cn[1];
9150 if(this.labelWidth > 12){
9151 labelCfg.style = "width: " + this.labelWidth + 'px';
9154 if(this.labelWidth < 13 && this.labelmd == 0){
9155 this.labelmd = this.labelWidth;
9158 if(this.labellg > 0){
9159 labelCfg.cls += ' col-lg-' + this.labellg;
9160 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9163 if(this.labelmd > 0){
9164 labelCfg.cls += ' col-md-' + this.labelmd;
9165 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9168 if(this.labelsm > 0){
9169 labelCfg.cls += ' col-sm-' + this.labelsm;
9170 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9173 if(this.labelxs > 0){
9174 labelCfg.cls += ' col-xs-' + this.labelxs;
9175 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9179 } else if ( this.fieldLabel.length) {
9184 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9185 tooltip : 'This field is required'
9189 //cls : 'input-group-addon',
9190 html : this.fieldLabel
9198 if(this.indicatorpos == 'right'){
9203 //cls : 'input-group-addon',
9204 html : this.fieldLabel
9209 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9210 tooltip : 'This field is required'
9230 if (this.parentType === 'Navbar' && this.parent().bar) {
9231 cfg.cls += ' navbar-form';
9234 if (this.parentType === 'NavGroup') {
9235 cfg.cls += ' navbar-form';
9243 * return the real input element.
9245 inputEl: function ()
9247 return this.el.select('input.form-control',true).first();
9250 tooltipEl : function()
9252 return this.inputEl();
9255 indicatorEl : function()
9257 var indicator = this.el.select('i.roo-required-indicator',true).first();
9267 setDisabled : function(v)
9269 var i = this.inputEl().dom;
9271 i.removeAttribute('disabled');
9275 i.setAttribute('disabled','true');
9277 initEvents : function()
9280 this.inputEl().on("keydown" , this.fireKey, this);
9281 this.inputEl().on("focus", this.onFocus, this);
9282 this.inputEl().on("blur", this.onBlur, this);
9284 this.inputEl().relayEvent('keyup', this);
9286 this.indicator = this.indicatorEl();
9289 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9292 // reference to original value for reset
9293 this.originalValue = this.getValue();
9294 //Roo.form.TextField.superclass.initEvents.call(this);
9295 if(this.validationEvent == 'keyup'){
9296 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9297 this.inputEl().on('keyup', this.filterValidation, this);
9299 else if(this.validationEvent !== false){
9300 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9303 if(this.selectOnFocus){
9304 this.on("focus", this.preFocus, this);
9307 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9308 this.inputEl().on("keypress", this.filterKeys, this);
9310 this.inputEl().relayEvent('keypress', this);
9313 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9314 this.el.on("click", this.autoSize, this);
9317 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9318 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9321 if (typeof(this.before) == 'object') {
9322 this.before.render(this.el.select('.roo-input-before',true).first());
9324 if (typeof(this.after) == 'object') {
9325 this.after.render(this.el.select('.roo-input-after',true).first());
9328 this.inputEl().on('change', this.onChange, this);
9331 filterValidation : function(e){
9332 if(!e.isNavKeyPress()){
9333 this.validationTask.delay(this.validationDelay);
9337 * Validates the field value
9338 * @return {Boolean} True if the value is valid, else false
9340 validate : function(){
9341 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9342 if(this.disabled || this.validateValue(this.getRawValue())){
9353 * Validates a value according to the field's validation rules and marks the field as invalid
9354 * if the validation fails
9355 * @param {Mixed} value The value to validate
9356 * @return {Boolean} True if the value is valid, else false
9358 validateValue : function(value)
9360 if(this.getVisibilityEl().hasClass('hidden')){
9364 if(value.length < 1) { // if it's blank
9365 if(this.allowBlank){
9371 if(value.length < this.minLength){
9374 if(value.length > this.maxLength){
9378 var vt = Roo.form.VTypes;
9379 if(!vt[this.vtype](value, this)){
9383 if(typeof this.validator == "function"){
9384 var msg = this.validator(value);
9388 if (typeof(msg) == 'string') {
9389 this.invalidText = msg;
9393 if(this.regex && !this.regex.test(value)){
9401 fireKey : function(e){
9402 //Roo.log('field ' + e.getKey());
9403 if(e.isNavKeyPress()){
9404 this.fireEvent("specialkey", this, e);
9407 focus : function (selectText){
9409 this.inputEl().focus();
9410 if(selectText === true){
9411 this.inputEl().dom.select();
9417 onFocus : function(){
9418 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9419 // this.el.addClass(this.focusClass);
9422 this.hasFocus = true;
9423 this.startValue = this.getValue();
9424 this.fireEvent("focus", this);
9428 beforeBlur : Roo.emptyFn,
9432 onBlur : function(){
9434 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9435 //this.el.removeClass(this.focusClass);
9437 this.hasFocus = false;
9438 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9441 var v = this.getValue();
9442 if(String(v) !== String(this.startValue)){
9443 this.fireEvent('change', this, v, this.startValue);
9445 this.fireEvent("blur", this);
9448 onChange : function(e)
9450 var v = this.getValue();
9451 if(String(v) !== String(this.startValue)){
9452 this.fireEvent('change', this, v, this.startValue);
9458 * Resets the current field value to the originally loaded value and clears any validation messages
9461 this.setValue(this.originalValue);
9465 * Returns the name of the field
9466 * @return {Mixed} name The name field
9468 getName: function(){
9472 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9473 * @return {Mixed} value The field value
9475 getValue : function(){
9477 var v = this.inputEl().getValue();
9482 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9483 * @return {Mixed} value The field value
9485 getRawValue : function(){
9486 var v = this.inputEl().getValue();
9492 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9493 * @param {Mixed} value The value to set
9495 setRawValue : function(v){
9496 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9499 selectText : function(start, end){
9500 var v = this.getRawValue();
9502 start = start === undefined ? 0 : start;
9503 end = end === undefined ? v.length : end;
9504 var d = this.inputEl().dom;
9505 if(d.setSelectionRange){
9506 d.setSelectionRange(start, end);
9507 }else if(d.createTextRange){
9508 var range = d.createTextRange();
9509 range.moveStart("character", start);
9510 range.moveEnd("character", v.length-end);
9517 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9518 * @param {Mixed} value The value to set
9520 setValue : function(v){
9523 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9529 processValue : function(value){
9530 if(this.stripCharsRe){
9531 var newValue = value.replace(this.stripCharsRe, '');
9532 if(newValue !== value){
9533 this.setRawValue(newValue);
9540 preFocus : function(){
9542 if(this.selectOnFocus){
9543 this.inputEl().dom.select();
9546 filterKeys : function(e){
9548 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9551 var c = e.getCharCode(), cc = String.fromCharCode(c);
9552 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9555 if(!this.maskRe.test(cc)){
9560 * Clear any invalid styles/messages for this field
9562 clearInvalid : function(){
9564 if(!this.el || this.preventMark){ // not rendered
9569 this.el.removeClass(this.invalidClass);
9571 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9573 var feedback = this.el.select('.form-control-feedback', true).first();
9576 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9582 this.indicator.removeClass('visible');
9583 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9586 this.fireEvent('valid', this);
9590 * Mark this field as valid
9592 markValid : function()
9594 if(!this.el || this.preventMark){ // not rendered...
9598 this.el.removeClass([this.invalidClass, this.validClass]);
9600 var feedback = this.el.select('.form-control-feedback', true).first();
9603 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9607 this.indicator.removeClass('visible');
9608 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9615 if(this.allowBlank && !this.getRawValue().length){
9619 this.el.addClass(this.validClass);
9621 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9623 var feedback = this.el.select('.form-control-feedback', true).first();
9626 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9627 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9632 this.fireEvent('valid', this);
9636 * Mark this field as invalid
9637 * @param {String} msg The validation message
9639 markInvalid : function(msg)
9641 if(!this.el || this.preventMark){ // not rendered
9645 this.el.removeClass([this.invalidClass, this.validClass]);
9647 var feedback = this.el.select('.form-control-feedback', true).first();
9650 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9657 if(this.allowBlank && !this.getRawValue().length){
9662 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9663 this.indicator.addClass('visible');
9666 this.el.addClass(this.invalidClass);
9668 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9670 var feedback = this.el.select('.form-control-feedback', true).first();
9673 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9675 if(this.getValue().length || this.forceFeedback){
9676 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9683 this.fireEvent('invalid', this, msg);
9686 SafariOnKeyDown : function(event)
9688 // this is a workaround for a password hang bug on chrome/ webkit.
9689 if (this.inputEl().dom.type != 'password') {
9693 var isSelectAll = false;
9695 if(this.inputEl().dom.selectionEnd > 0){
9696 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9698 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9699 event.preventDefault();
9704 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9706 event.preventDefault();
9707 // this is very hacky as keydown always get's upper case.
9709 var cc = String.fromCharCode(event.getCharCode());
9710 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9714 adjustWidth : function(tag, w){
9715 tag = tag.toLowerCase();
9716 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9717 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9721 if(tag == 'textarea'){
9724 }else if(Roo.isOpera){
9728 if(tag == 'textarea'){
9736 setFieldLabel : function(v)
9743 var ar = this.el.select('label > span',true);
9745 if (ar.elements.length) {
9746 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9747 this.fieldLabel = v;
9751 var br = this.el.select('label',true);
9753 if(br.elements.length) {
9754 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9755 this.fieldLabel = v;
9759 Roo.log('Cannot Found any of label > span || label in input');
9763 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9764 this.fieldLabel = v;
9779 * @class Roo.bootstrap.TextArea
9780 * @extends Roo.bootstrap.Input
9781 * Bootstrap TextArea class
9782 * @cfg {Number} cols Specifies the visible width of a text area
9783 * @cfg {Number} rows Specifies the visible number of lines in a text area
9784 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9785 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9786 * @cfg {string} html text
9789 * Create a new TextArea
9790 * @param {Object} config The config object
9793 Roo.bootstrap.TextArea = function(config){
9794 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9798 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9808 getAutoCreate : function(){
9810 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9816 if(this.inputType != 'hidden'){
9817 cfg.cls = 'form-group' //input-group
9825 value : this.value || '',
9826 html: this.html || '',
9827 cls : 'form-control',
9828 placeholder : this.placeholder || ''
9832 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9833 input.maxLength = this.maxLength;
9837 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9841 input.cols = this.cols;
9844 if (this.readOnly) {
9845 input.readonly = true;
9849 input.name = this.name;
9853 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9857 ['xs','sm','md','lg'].map(function(size){
9858 if (settings[size]) {
9859 cfg.cls += ' col-' + size + '-' + settings[size];
9863 var inputblock = input;
9865 if(this.hasFeedback && !this.allowBlank){
9869 cls: 'glyphicon form-control-feedback'
9873 cls : 'has-feedback',
9882 if (this.before || this.after) {
9885 cls : 'input-group',
9889 inputblock.cn.push({
9891 cls : 'input-group-addon',
9896 inputblock.cn.push(input);
9898 if(this.hasFeedback && !this.allowBlank){
9899 inputblock.cls += ' has-feedback';
9900 inputblock.cn.push(feedback);
9904 inputblock.cn.push({
9906 cls : 'input-group-addon',
9913 if (align ==='left' && this.fieldLabel.length) {
9918 cls : 'control-label',
9919 html : this.fieldLabel
9930 if(this.labelWidth > 12){
9931 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9934 if(this.labelWidth < 13 && this.labelmd == 0){
9935 this.labelmd = this.labelWidth;
9938 if(this.labellg > 0){
9939 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9940 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9943 if(this.labelmd > 0){
9944 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9945 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9948 if(this.labelsm > 0){
9949 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9950 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9953 if(this.labelxs > 0){
9954 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9955 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9958 } else if ( this.fieldLabel.length) {
9963 //cls : 'input-group-addon',
9964 html : this.fieldLabel
9982 if (this.disabled) {
9983 input.disabled=true;
9990 * return the real textarea element.
9992 inputEl: function ()
9994 return this.el.select('textarea.form-control',true).first();
9998 * Clear any invalid styles/messages for this field
10000 clearInvalid : function()
10003 if(!this.el || this.preventMark){ // not rendered
10007 var label = this.el.select('label', true).first();
10008 var icon = this.el.select('i.fa-star', true).first();
10014 this.el.removeClass(this.invalidClass);
10016 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10018 var feedback = this.el.select('.form-control-feedback', true).first();
10021 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10026 this.fireEvent('valid', this);
10030 * Mark this field as valid
10032 markValid : function()
10034 if(!this.el || this.preventMark){ // not rendered
10038 this.el.removeClass([this.invalidClass, this.validClass]);
10040 var feedback = this.el.select('.form-control-feedback', true).first();
10043 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10046 if(this.disabled || this.allowBlank){
10050 var label = this.el.select('label', true).first();
10051 var icon = this.el.select('i.fa-star', true).first();
10057 this.el.addClass(this.validClass);
10059 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10061 var feedback = this.el.select('.form-control-feedback', true).first();
10064 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10065 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10070 this.fireEvent('valid', this);
10074 * Mark this field as invalid
10075 * @param {String} msg The validation message
10077 markInvalid : function(msg)
10079 if(!this.el || this.preventMark){ // not rendered
10083 this.el.removeClass([this.invalidClass, this.validClass]);
10085 var feedback = this.el.select('.form-control-feedback', true).first();
10088 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10091 if(this.disabled || this.allowBlank){
10095 var label = this.el.select('label', true).first();
10096 var icon = this.el.select('i.fa-star', true).first();
10098 if(!this.getValue().length && label && !icon){
10099 this.el.createChild({
10101 cls : 'text-danger fa fa-lg fa-star',
10102 tooltip : 'This field is required',
10103 style : 'margin-right:5px;'
10107 this.el.addClass(this.invalidClass);
10109 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10111 var feedback = this.el.select('.form-control-feedback', true).first();
10114 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10116 if(this.getValue().length || this.forceFeedback){
10117 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10124 this.fireEvent('invalid', this, msg);
10132 * trigger field - base class for combo..
10137 * @class Roo.bootstrap.TriggerField
10138 * @extends Roo.bootstrap.Input
10139 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10140 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10141 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10142 * for which you can provide a custom implementation. For example:
10144 var trigger = new Roo.bootstrap.TriggerField();
10145 trigger.onTriggerClick = myTriggerFn;
10146 trigger.applyTo('my-field');
10149 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10150 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10151 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10152 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10153 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10156 * Create a new TriggerField.
10157 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10158 * to the base TextField)
10160 Roo.bootstrap.TriggerField = function(config){
10161 this.mimicing = false;
10162 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10165 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10167 * @cfg {String} triggerClass A CSS class to apply to the trigger
10170 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10175 * @cfg {Boolean} removable (true|false) special filter default false
10179 /** @cfg {Boolean} grow @hide */
10180 /** @cfg {Number} growMin @hide */
10181 /** @cfg {Number} growMax @hide */
10187 autoSize: Roo.emptyFn,
10191 deferHeight : true,
10194 actionMode : 'wrap',
10199 getAutoCreate : function(){
10201 var align = this.labelAlign || this.parentLabelAlign();
10206 cls: 'form-group' //input-group
10213 type : this.inputType,
10214 cls : 'form-control',
10215 autocomplete: 'new-password',
10216 placeholder : this.placeholder || ''
10220 input.name = this.name;
10223 input.cls += ' input-' + this.size;
10226 if (this.disabled) {
10227 input.disabled=true;
10230 var inputblock = input;
10232 if(this.hasFeedback && !this.allowBlank){
10236 cls: 'glyphicon form-control-feedback'
10239 if(this.removable && !this.editable && !this.tickable){
10241 cls : 'has-feedback',
10247 cls : 'roo-combo-removable-btn close'
10254 cls : 'has-feedback',
10263 if(this.removable && !this.editable && !this.tickable){
10265 cls : 'roo-removable',
10271 cls : 'roo-combo-removable-btn close'
10278 if (this.before || this.after) {
10281 cls : 'input-group',
10285 inputblock.cn.push({
10287 cls : 'input-group-addon',
10292 inputblock.cn.push(input);
10294 if(this.hasFeedback && !this.allowBlank){
10295 inputblock.cls += ' has-feedback';
10296 inputblock.cn.push(feedback);
10300 inputblock.cn.push({
10302 cls : 'input-group-addon',
10315 cls: 'form-hidden-field'
10329 cls: 'form-hidden-field'
10333 cls: 'roo-select2-choices',
10337 cls: 'roo-select2-search-field',
10350 cls: 'roo-select2-container input-group',
10355 // cls: 'typeahead typeahead-long dropdown-menu',
10356 // style: 'display:none'
10361 if(!this.multiple && this.showToggleBtn){
10367 if (this.caret != false) {
10370 cls: 'fa fa-' + this.caret
10377 cls : 'input-group-addon btn dropdown-toggle',
10382 cls: 'combobox-clear',
10396 combobox.cls += ' roo-select2-container-multi';
10399 if (align ==='left' && this.fieldLabel.length) {
10401 cfg.cls += ' roo-form-group-label-left';
10406 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10407 tooltip : 'This field is required'
10412 cls : 'control-label',
10413 html : this.fieldLabel
10425 var labelCfg = cfg.cn[1];
10426 var contentCfg = cfg.cn[2];
10428 if(this.indicatorpos == 'right'){
10433 cls : 'control-label',
10437 html : this.fieldLabel
10441 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10442 tooltip : 'This field is required'
10455 labelCfg = cfg.cn[0];
10456 contentCfg = cfg.cn[1];
10459 if(this.labelWidth > 12){
10460 labelCfg.style = "width: " + this.labelWidth + 'px';
10463 if(this.labelWidth < 13 && this.labelmd == 0){
10464 this.labelmd = this.labelWidth;
10467 if(this.labellg > 0){
10468 labelCfg.cls += ' col-lg-' + this.labellg;
10469 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10472 if(this.labelmd > 0){
10473 labelCfg.cls += ' col-md-' + this.labelmd;
10474 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10477 if(this.labelsm > 0){
10478 labelCfg.cls += ' col-sm-' + this.labelsm;
10479 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10482 if(this.labelxs > 0){
10483 labelCfg.cls += ' col-xs-' + this.labelxs;
10484 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10487 } else if ( this.fieldLabel.length) {
10488 // Roo.log(" label");
10492 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10493 tooltip : 'This field is required'
10497 //cls : 'input-group-addon',
10498 html : this.fieldLabel
10506 if(this.indicatorpos == 'right'){
10514 html : this.fieldLabel
10518 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10519 tooltip : 'This field is required'
10532 // Roo.log(" no label && no align");
10539 ['xs','sm','md','lg'].map(function(size){
10540 if (settings[size]) {
10541 cfg.cls += ' col-' + size + '-' + settings[size];
10552 onResize : function(w, h){
10553 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10554 // if(typeof w == 'number'){
10555 // var x = w - this.trigger.getWidth();
10556 // this.inputEl().setWidth(this.adjustWidth('input', x));
10557 // this.trigger.setStyle('left', x+'px');
10562 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10565 getResizeEl : function(){
10566 return this.inputEl();
10570 getPositionEl : function(){
10571 return this.inputEl();
10575 alignErrorIcon : function(){
10576 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10580 initEvents : function(){
10584 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10585 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10586 if(!this.multiple && this.showToggleBtn){
10587 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10588 if(this.hideTrigger){
10589 this.trigger.setDisplayed(false);
10591 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10595 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10598 if(this.removable && !this.editable && !this.tickable){
10599 var close = this.closeTriggerEl();
10602 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10603 close.on('click', this.removeBtnClick, this, close);
10607 //this.trigger.addClassOnOver('x-form-trigger-over');
10608 //this.trigger.addClassOnClick('x-form-trigger-click');
10611 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10615 closeTriggerEl : function()
10617 var close = this.el.select('.roo-combo-removable-btn', true).first();
10618 return close ? close : false;
10621 removeBtnClick : function(e, h, el)
10623 e.preventDefault();
10625 if(this.fireEvent("remove", this) !== false){
10627 this.fireEvent("afterremove", this)
10631 createList : function()
10633 this.list = Roo.get(document.body).createChild({
10635 cls: 'typeahead typeahead-long dropdown-menu',
10636 style: 'display:none'
10639 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10644 initTrigger : function(){
10649 onDestroy : function(){
10651 this.trigger.removeAllListeners();
10652 // this.trigger.remove();
10655 // this.wrap.remove();
10657 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10661 onFocus : function(){
10662 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10664 if(!this.mimicing){
10665 this.wrap.addClass('x-trigger-wrap-focus');
10666 this.mimicing = true;
10667 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10668 if(this.monitorTab){
10669 this.el.on("keydown", this.checkTab, this);
10676 checkTab : function(e){
10677 if(e.getKey() == e.TAB){
10678 this.triggerBlur();
10683 onBlur : function(){
10688 mimicBlur : function(e, t){
10690 if(!this.wrap.contains(t) && this.validateBlur()){
10691 this.triggerBlur();
10697 triggerBlur : function(){
10698 this.mimicing = false;
10699 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10700 if(this.monitorTab){
10701 this.el.un("keydown", this.checkTab, this);
10703 //this.wrap.removeClass('x-trigger-wrap-focus');
10704 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10708 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10709 validateBlur : function(e, t){
10714 onDisable : function(){
10715 this.inputEl().dom.disabled = true;
10716 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10718 // this.wrap.addClass('x-item-disabled');
10723 onEnable : function(){
10724 this.inputEl().dom.disabled = false;
10725 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10727 // this.el.removeClass('x-item-disabled');
10732 onShow : function(){
10733 var ae = this.getActionEl();
10736 ae.dom.style.display = '';
10737 ae.dom.style.visibility = 'visible';
10743 onHide : function(){
10744 var ae = this.getActionEl();
10745 ae.dom.style.display = 'none';
10749 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10750 * by an implementing function.
10752 * @param {EventObject} e
10754 onTriggerClick : Roo.emptyFn
10758 * Ext JS Library 1.1.1
10759 * Copyright(c) 2006-2007, Ext JS, LLC.
10761 * Originally Released Under LGPL - original licence link has changed is not relivant.
10764 * <script type="text/javascript">
10769 * @class Roo.data.SortTypes
10771 * Defines the default sorting (casting?) comparison functions used when sorting data.
10773 Roo.data.SortTypes = {
10775 * Default sort that does nothing
10776 * @param {Mixed} s The value being converted
10777 * @return {Mixed} The comparison value
10779 none : function(s){
10784 * The regular expression used to strip tags
10788 stripTagsRE : /<\/?[^>]+>/gi,
10791 * Strips all HTML tags to sort on text only
10792 * @param {Mixed} s The value being converted
10793 * @return {String} The comparison value
10795 asText : function(s){
10796 return String(s).replace(this.stripTagsRE, "");
10800 * Strips all HTML tags to sort on text only - Case insensitive
10801 * @param {Mixed} s The value being converted
10802 * @return {String} The comparison value
10804 asUCText : function(s){
10805 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10809 * Case insensitive string
10810 * @param {Mixed} s The value being converted
10811 * @return {String} The comparison value
10813 asUCString : function(s) {
10814 return String(s).toUpperCase();
10819 * @param {Mixed} s The value being converted
10820 * @return {Number} The comparison value
10822 asDate : function(s) {
10826 if(s instanceof Date){
10827 return s.getTime();
10829 return Date.parse(String(s));
10834 * @param {Mixed} s The value being converted
10835 * @return {Float} The comparison value
10837 asFloat : function(s) {
10838 var val = parseFloat(String(s).replace(/,/g, ""));
10847 * @param {Mixed} s The value being converted
10848 * @return {Number} The comparison value
10850 asInt : function(s) {
10851 var val = parseInt(String(s).replace(/,/g, ""));
10859 * Ext JS Library 1.1.1
10860 * Copyright(c) 2006-2007, Ext JS, LLC.
10862 * Originally Released Under LGPL - original licence link has changed is not relivant.
10865 * <script type="text/javascript">
10869 * @class Roo.data.Record
10870 * Instances of this class encapsulate both record <em>definition</em> information, and record
10871 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10872 * to access Records cached in an {@link Roo.data.Store} object.<br>
10874 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10875 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10878 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10880 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10881 * {@link #create}. The parameters are the same.
10882 * @param {Array} data An associative Array of data values keyed by the field name.
10883 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10884 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10885 * not specified an integer id is generated.
10887 Roo.data.Record = function(data, id){
10888 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10893 * Generate a constructor for a specific record layout.
10894 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10895 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10896 * Each field definition object may contain the following properties: <ul>
10897 * <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,
10898 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10899 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10900 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10901 * is being used, then this is a string containing the javascript expression to reference the data relative to
10902 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10903 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10904 * this may be omitted.</p></li>
10905 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10906 * <ul><li>auto (Default, implies no conversion)</li>
10911 * <li>date</li></ul></p></li>
10912 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10913 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10914 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10915 * by the Reader into an object that will be stored in the Record. It is passed the
10916 * following parameters:<ul>
10917 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10919 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10921 * <br>usage:<br><pre><code>
10922 var TopicRecord = Roo.data.Record.create(
10923 {name: 'title', mapping: 'topic_title'},
10924 {name: 'author', mapping: 'username'},
10925 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10926 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10927 {name: 'lastPoster', mapping: 'user2'},
10928 {name: 'excerpt', mapping: 'post_text'}
10931 var myNewRecord = new TopicRecord({
10932 title: 'Do my job please',
10935 lastPost: new Date(),
10936 lastPoster: 'Animal',
10937 excerpt: 'No way dude!'
10939 myStore.add(myNewRecord);
10944 Roo.data.Record.create = function(o){
10945 var f = function(){
10946 f.superclass.constructor.apply(this, arguments);
10948 Roo.extend(f, Roo.data.Record);
10949 var p = f.prototype;
10950 p.fields = new Roo.util.MixedCollection(false, function(field){
10953 for(var i = 0, len = o.length; i < len; i++){
10954 p.fields.add(new Roo.data.Field(o[i]));
10956 f.getField = function(name){
10957 return p.fields.get(name);
10962 Roo.data.Record.AUTO_ID = 1000;
10963 Roo.data.Record.EDIT = 'edit';
10964 Roo.data.Record.REJECT = 'reject';
10965 Roo.data.Record.COMMIT = 'commit';
10967 Roo.data.Record.prototype = {
10969 * Readonly flag - true if this record has been modified.
10978 join : function(store){
10979 this.store = store;
10983 * Set the named field to the specified value.
10984 * @param {String} name The name of the field to set.
10985 * @param {Object} value The value to set the field to.
10987 set : function(name, value){
10988 if(this.data[name] == value){
10992 if(!this.modified){
10993 this.modified = {};
10995 if(typeof this.modified[name] == 'undefined'){
10996 this.modified[name] = this.data[name];
10998 this.data[name] = value;
10999 if(!this.editing && this.store){
11000 this.store.afterEdit(this);
11005 * Get the value of the named field.
11006 * @param {String} name The name of the field to get the value of.
11007 * @return {Object} The value of the field.
11009 get : function(name){
11010 return this.data[name];
11014 beginEdit : function(){
11015 this.editing = true;
11016 this.modified = {};
11020 cancelEdit : function(){
11021 this.editing = false;
11022 delete this.modified;
11026 endEdit : function(){
11027 this.editing = false;
11028 if(this.dirty && this.store){
11029 this.store.afterEdit(this);
11034 * Usually called by the {@link Roo.data.Store} which owns the Record.
11035 * Rejects all changes made to the Record since either creation, or the last commit operation.
11036 * Modified fields are reverted to their original values.
11038 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11039 * of reject operations.
11041 reject : function(){
11042 var m = this.modified;
11044 if(typeof m[n] != "function"){
11045 this.data[n] = m[n];
11048 this.dirty = false;
11049 delete this.modified;
11050 this.editing = false;
11052 this.store.afterReject(this);
11057 * Usually called by the {@link Roo.data.Store} which owns the Record.
11058 * Commits all changes made to the Record since either creation, or the last commit operation.
11060 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11061 * of commit operations.
11063 commit : function(){
11064 this.dirty = false;
11065 delete this.modified;
11066 this.editing = false;
11068 this.store.afterCommit(this);
11073 hasError : function(){
11074 return this.error != null;
11078 clearError : function(){
11083 * Creates a copy of this record.
11084 * @param {String} id (optional) A new record id if you don't want to use this record's id
11087 copy : function(newId) {
11088 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11092 * Ext JS Library 1.1.1
11093 * Copyright(c) 2006-2007, Ext JS, LLC.
11095 * Originally Released Under LGPL - original licence link has changed is not relivant.
11098 * <script type="text/javascript">
11104 * @class Roo.data.Store
11105 * @extends Roo.util.Observable
11106 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11107 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11109 * 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
11110 * has no knowledge of the format of the data returned by the Proxy.<br>
11112 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11113 * instances from the data object. These records are cached and made available through accessor functions.
11115 * Creates a new Store.
11116 * @param {Object} config A config object containing the objects needed for the Store to access data,
11117 * and read the data into Records.
11119 Roo.data.Store = function(config){
11120 this.data = new Roo.util.MixedCollection(false);
11121 this.data.getKey = function(o){
11124 this.baseParams = {};
11126 this.paramNames = {
11131 "multisort" : "_multisort"
11134 if(config && config.data){
11135 this.inlineData = config.data;
11136 delete config.data;
11139 Roo.apply(this, config);
11141 if(this.reader){ // reader passed
11142 this.reader = Roo.factory(this.reader, Roo.data);
11143 this.reader.xmodule = this.xmodule || false;
11144 if(!this.recordType){
11145 this.recordType = this.reader.recordType;
11147 if(this.reader.onMetaChange){
11148 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11152 if(this.recordType){
11153 this.fields = this.recordType.prototype.fields;
11155 this.modified = [];
11159 * @event datachanged
11160 * Fires when the data cache has changed, and a widget which is using this Store
11161 * as a Record cache should refresh its view.
11162 * @param {Store} this
11164 datachanged : true,
11166 * @event metachange
11167 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11168 * @param {Store} this
11169 * @param {Object} meta The JSON metadata
11174 * Fires when Records have been added to the Store
11175 * @param {Store} this
11176 * @param {Roo.data.Record[]} records The array of Records added
11177 * @param {Number} index The index at which the record(s) were added
11182 * Fires when a Record has been removed from the Store
11183 * @param {Store} this
11184 * @param {Roo.data.Record} record The Record that was removed
11185 * @param {Number} index The index at which the record was removed
11190 * Fires when a Record has been updated
11191 * @param {Store} this
11192 * @param {Roo.data.Record} record The Record that was updated
11193 * @param {String} operation The update operation being performed. Value may be one of:
11195 Roo.data.Record.EDIT
11196 Roo.data.Record.REJECT
11197 Roo.data.Record.COMMIT
11203 * Fires when the data cache has been cleared.
11204 * @param {Store} this
11208 * @event beforeload
11209 * Fires before a request is made for a new data object. If the beforeload handler returns false
11210 * the load action will be canceled.
11211 * @param {Store} this
11212 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11216 * @event beforeloadadd
11217 * Fires after a new set of Records has been loaded.
11218 * @param {Store} this
11219 * @param {Roo.data.Record[]} records The Records that were loaded
11220 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11222 beforeloadadd : true,
11225 * Fires after a new set of Records has been loaded, before they are added to the store.
11226 * @param {Store} this
11227 * @param {Roo.data.Record[]} records The Records that were loaded
11228 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11229 * @params {Object} return from reader
11233 * @event loadexception
11234 * Fires if an exception occurs in the Proxy during loading.
11235 * Called with the signature of the Proxy's "loadexception" event.
11236 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11239 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11240 * @param {Object} load options
11241 * @param {Object} jsonData from your request (normally this contains the Exception)
11243 loadexception : true
11247 this.proxy = Roo.factory(this.proxy, Roo.data);
11248 this.proxy.xmodule = this.xmodule || false;
11249 this.relayEvents(this.proxy, ["loadexception"]);
11251 this.sortToggle = {};
11252 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11254 Roo.data.Store.superclass.constructor.call(this);
11256 if(this.inlineData){
11257 this.loadData(this.inlineData);
11258 delete this.inlineData;
11262 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11264 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11265 * without a remote query - used by combo/forms at present.
11269 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11272 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11275 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11276 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11279 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11280 * on any HTTP request
11283 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11286 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11290 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11291 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11293 remoteSort : false,
11296 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11297 * loaded or when a record is removed. (defaults to false).
11299 pruneModifiedRecords : false,
11302 lastOptions : null,
11305 * Add Records to the Store and fires the add event.
11306 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11308 add : function(records){
11309 records = [].concat(records);
11310 for(var i = 0, len = records.length; i < len; i++){
11311 records[i].join(this);
11313 var index = this.data.length;
11314 this.data.addAll(records);
11315 this.fireEvent("add", this, records, index);
11319 * Remove a Record from the Store and fires the remove event.
11320 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11322 remove : function(record){
11323 var index = this.data.indexOf(record);
11324 this.data.removeAt(index);
11326 if(this.pruneModifiedRecords){
11327 this.modified.remove(record);
11329 this.fireEvent("remove", this, record, index);
11333 * Remove all Records from the Store and fires the clear event.
11335 removeAll : function(){
11337 if(this.pruneModifiedRecords){
11338 this.modified = [];
11340 this.fireEvent("clear", this);
11344 * Inserts Records to the Store at the given index and fires the add event.
11345 * @param {Number} index The start index at which to insert the passed Records.
11346 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11348 insert : function(index, records){
11349 records = [].concat(records);
11350 for(var i = 0, len = records.length; i < len; i++){
11351 this.data.insert(index, records[i]);
11352 records[i].join(this);
11354 this.fireEvent("add", this, records, index);
11358 * Get the index within the cache of the passed Record.
11359 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11360 * @return {Number} The index of the passed Record. Returns -1 if not found.
11362 indexOf : function(record){
11363 return this.data.indexOf(record);
11367 * Get the index within the cache of the Record with the passed id.
11368 * @param {String} id The id of the Record to find.
11369 * @return {Number} The index of the Record. Returns -1 if not found.
11371 indexOfId : function(id){
11372 return this.data.indexOfKey(id);
11376 * Get the Record with the specified id.
11377 * @param {String} id The id of the Record to find.
11378 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11380 getById : function(id){
11381 return this.data.key(id);
11385 * Get the Record at the specified index.
11386 * @param {Number} index The index of the Record to find.
11387 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11389 getAt : function(index){
11390 return this.data.itemAt(index);
11394 * Returns a range of Records between specified indices.
11395 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11396 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11397 * @return {Roo.data.Record[]} An array of Records
11399 getRange : function(start, end){
11400 return this.data.getRange(start, end);
11404 storeOptions : function(o){
11405 o = Roo.apply({}, o);
11408 this.lastOptions = o;
11412 * Loads the Record cache from the configured Proxy using the configured Reader.
11414 * If using remote paging, then the first load call must specify the <em>start</em>
11415 * and <em>limit</em> properties in the options.params property to establish the initial
11416 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11418 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11419 * and this call will return before the new data has been loaded. Perform any post-processing
11420 * in a callback function, or in a "load" event handler.</strong>
11422 * @param {Object} options An object containing properties which control loading options:<ul>
11423 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11424 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11425 * passed the following arguments:<ul>
11426 * <li>r : Roo.data.Record[]</li>
11427 * <li>options: Options object from the load call</li>
11428 * <li>success: Boolean success indicator</li></ul></li>
11429 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11430 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11433 load : function(options){
11434 options = options || {};
11435 if(this.fireEvent("beforeload", this, options) !== false){
11436 this.storeOptions(options);
11437 var p = Roo.apply(options.params || {}, this.baseParams);
11438 // if meta was not loaded from remote source.. try requesting it.
11439 if (!this.reader.metaFromRemote) {
11440 p._requestMeta = 1;
11442 if(this.sortInfo && this.remoteSort){
11443 var pn = this.paramNames;
11444 p[pn["sort"]] = this.sortInfo.field;
11445 p[pn["dir"]] = this.sortInfo.direction;
11447 if (this.multiSort) {
11448 var pn = this.paramNames;
11449 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11452 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11457 * Reloads the Record cache from the configured Proxy using the configured Reader and
11458 * the options from the last load operation performed.
11459 * @param {Object} options (optional) An object containing properties which may override the options
11460 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11461 * the most recently used options are reused).
11463 reload : function(options){
11464 this.load(Roo.applyIf(options||{}, this.lastOptions));
11468 // Called as a callback by the Reader during a load operation.
11469 loadRecords : function(o, options, success){
11470 if(!o || success === false){
11471 if(success !== false){
11472 this.fireEvent("load", this, [], options, o);
11474 if(options.callback){
11475 options.callback.call(options.scope || this, [], options, false);
11479 // if data returned failure - throw an exception.
11480 if (o.success === false) {
11481 // show a message if no listener is registered.
11482 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11483 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11485 // loadmask wil be hooked into this..
11486 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11489 var r = o.records, t = o.totalRecords || r.length;
11491 this.fireEvent("beforeloadadd", this, r, options, o);
11493 if(!options || options.add !== true){
11494 if(this.pruneModifiedRecords){
11495 this.modified = [];
11497 for(var i = 0, len = r.length; i < len; i++){
11501 this.data = this.snapshot;
11502 delete this.snapshot;
11505 this.data.addAll(r);
11506 this.totalLength = t;
11508 this.fireEvent("datachanged", this);
11510 this.totalLength = Math.max(t, this.data.length+r.length);
11514 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11516 var e = new Roo.data.Record({});
11518 e.set(this.parent.displayField, this.parent.emptyTitle);
11519 e.set(this.parent.valueField, '');
11524 this.fireEvent("load", this, r, options, o);
11525 if(options.callback){
11526 options.callback.call(options.scope || this, r, options, true);
11532 * Loads data from a passed data block. A Reader which understands the format of the data
11533 * must have been configured in the constructor.
11534 * @param {Object} data The data block from which to read the Records. The format of the data expected
11535 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11536 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11538 loadData : function(o, append){
11539 var r = this.reader.readRecords(o);
11540 this.loadRecords(r, {add: append}, true);
11544 * Gets the number of cached records.
11546 * <em>If using paging, this may not be the total size of the dataset. If the data object
11547 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11548 * the data set size</em>
11550 getCount : function(){
11551 return this.data.length || 0;
11555 * Gets the total number of records in the dataset as returned by the server.
11557 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11558 * the dataset size</em>
11560 getTotalCount : function(){
11561 return this.totalLength || 0;
11565 * Returns the sort state of the Store as an object with two properties:
11567 field {String} The name of the field by which the Records are sorted
11568 direction {String} The sort order, "ASC" or "DESC"
11571 getSortState : function(){
11572 return this.sortInfo;
11576 applySort : function(){
11577 if(this.sortInfo && !this.remoteSort){
11578 var s = this.sortInfo, f = s.field;
11579 var st = this.fields.get(f).sortType;
11580 var fn = function(r1, r2){
11581 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11582 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11584 this.data.sort(s.direction, fn);
11585 if(this.snapshot && this.snapshot != this.data){
11586 this.snapshot.sort(s.direction, fn);
11592 * Sets the default sort column and order to be used by the next load operation.
11593 * @param {String} fieldName The name of the field to sort by.
11594 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11596 setDefaultSort : function(field, dir){
11597 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11601 * Sort the Records.
11602 * If remote sorting is used, the sort is performed on the server, and the cache is
11603 * reloaded. If local sorting is used, the cache is sorted internally.
11604 * @param {String} fieldName The name of the field to sort by.
11605 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11607 sort : function(fieldName, dir){
11608 var f = this.fields.get(fieldName);
11610 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11612 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11613 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11618 this.sortToggle[f.name] = dir;
11619 this.sortInfo = {field: f.name, direction: dir};
11620 if(!this.remoteSort){
11622 this.fireEvent("datachanged", this);
11624 this.load(this.lastOptions);
11629 * Calls the specified function for each of the Records in the cache.
11630 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11631 * Returning <em>false</em> aborts and exits the iteration.
11632 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11634 each : function(fn, scope){
11635 this.data.each(fn, scope);
11639 * Gets all records modified since the last commit. Modified records are persisted across load operations
11640 * (e.g., during paging).
11641 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11643 getModifiedRecords : function(){
11644 return this.modified;
11648 createFilterFn : function(property, value, anyMatch){
11649 if(!value.exec){ // not a regex
11650 value = String(value);
11651 if(value.length == 0){
11654 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11656 return function(r){
11657 return value.test(r.data[property]);
11662 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11663 * @param {String} property A field on your records
11664 * @param {Number} start The record index to start at (defaults to 0)
11665 * @param {Number} end The last record index to include (defaults to length - 1)
11666 * @return {Number} The sum
11668 sum : function(property, start, end){
11669 var rs = this.data.items, v = 0;
11670 start = start || 0;
11671 end = (end || end === 0) ? end : rs.length-1;
11673 for(var i = start; i <= end; i++){
11674 v += (rs[i].data[property] || 0);
11680 * Filter the records by a specified property.
11681 * @param {String} field A field on your records
11682 * @param {String/RegExp} value Either a string that the field
11683 * should start with or a RegExp to test against the field
11684 * @param {Boolean} anyMatch True to match any part not just the beginning
11686 filter : function(property, value, anyMatch){
11687 var fn = this.createFilterFn(property, value, anyMatch);
11688 return fn ? this.filterBy(fn) : this.clearFilter();
11692 * Filter by a function. The specified function will be called with each
11693 * record in this data source. If the function returns true the record is included,
11694 * otherwise it is filtered.
11695 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11696 * @param {Object} scope (optional) The scope of the function (defaults to this)
11698 filterBy : function(fn, scope){
11699 this.snapshot = this.snapshot || this.data;
11700 this.data = this.queryBy(fn, scope||this);
11701 this.fireEvent("datachanged", this);
11705 * Query the records by a specified property.
11706 * @param {String} field A field on your records
11707 * @param {String/RegExp} value Either a string that the field
11708 * should start with or a RegExp to test against the field
11709 * @param {Boolean} anyMatch True to match any part not just the beginning
11710 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11712 query : function(property, value, anyMatch){
11713 var fn = this.createFilterFn(property, value, anyMatch);
11714 return fn ? this.queryBy(fn) : this.data.clone();
11718 * Query by a function. The specified function will be called with each
11719 * record in this data source. If the function returns true the record is included
11721 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11722 * @param {Object} scope (optional) The scope of the function (defaults to this)
11723 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11725 queryBy : function(fn, scope){
11726 var data = this.snapshot || this.data;
11727 return data.filterBy(fn, scope||this);
11731 * Collects unique values for a particular dataIndex from this store.
11732 * @param {String} dataIndex The property to collect
11733 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11734 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11735 * @return {Array} An array of the unique values
11737 collect : function(dataIndex, allowNull, bypassFilter){
11738 var d = (bypassFilter === true && this.snapshot) ?
11739 this.snapshot.items : this.data.items;
11740 var v, sv, r = [], l = {};
11741 for(var i = 0, len = d.length; i < len; i++){
11742 v = d[i].data[dataIndex];
11744 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11753 * Revert to a view of the Record cache with no filtering applied.
11754 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11756 clearFilter : function(suppressEvent){
11757 if(this.snapshot && this.snapshot != this.data){
11758 this.data = this.snapshot;
11759 delete this.snapshot;
11760 if(suppressEvent !== true){
11761 this.fireEvent("datachanged", this);
11767 afterEdit : function(record){
11768 if(this.modified.indexOf(record) == -1){
11769 this.modified.push(record);
11771 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11775 afterReject : function(record){
11776 this.modified.remove(record);
11777 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11781 afterCommit : function(record){
11782 this.modified.remove(record);
11783 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11787 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11788 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11790 commitChanges : function(){
11791 var m = this.modified.slice(0);
11792 this.modified = [];
11793 for(var i = 0, len = m.length; i < len; i++){
11799 * Cancel outstanding changes on all changed records.
11801 rejectChanges : function(){
11802 var m = this.modified.slice(0);
11803 this.modified = [];
11804 for(var i = 0, len = m.length; i < len; i++){
11809 onMetaChange : function(meta, rtype, o){
11810 this.recordType = rtype;
11811 this.fields = rtype.prototype.fields;
11812 delete this.snapshot;
11813 this.sortInfo = meta.sortInfo || this.sortInfo;
11814 this.modified = [];
11815 this.fireEvent('metachange', this, this.reader.meta);
11818 moveIndex : function(data, type)
11820 var index = this.indexOf(data);
11822 var newIndex = index + type;
11826 this.insert(newIndex, data);
11831 * Ext JS Library 1.1.1
11832 * Copyright(c) 2006-2007, Ext JS, LLC.
11834 * Originally Released Under LGPL - original licence link has changed is not relivant.
11837 * <script type="text/javascript">
11841 * @class Roo.data.SimpleStore
11842 * @extends Roo.data.Store
11843 * Small helper class to make creating Stores from Array data easier.
11844 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11845 * @cfg {Array} fields An array of field definition objects, or field name strings.
11846 * @cfg {Array} data The multi-dimensional array of data
11848 * @param {Object} config
11850 Roo.data.SimpleStore = function(config){
11851 Roo.data.SimpleStore.superclass.constructor.call(this, {
11853 reader: new Roo.data.ArrayReader({
11856 Roo.data.Record.create(config.fields)
11858 proxy : new Roo.data.MemoryProxy(config.data)
11862 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11864 * Ext JS Library 1.1.1
11865 * Copyright(c) 2006-2007, Ext JS, LLC.
11867 * Originally Released Under LGPL - original licence link has changed is not relivant.
11870 * <script type="text/javascript">
11875 * @extends Roo.data.Store
11876 * @class Roo.data.JsonStore
11877 * Small helper class to make creating Stores for JSON data easier. <br/>
11879 var store = new Roo.data.JsonStore({
11880 url: 'get-images.php',
11882 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11885 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11886 * JsonReader and HttpProxy (unless inline data is provided).</b>
11887 * @cfg {Array} fields An array of field definition objects, or field name strings.
11889 * @param {Object} config
11891 Roo.data.JsonStore = function(c){
11892 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11893 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11894 reader: new Roo.data.JsonReader(c, c.fields)
11897 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11899 * Ext JS Library 1.1.1
11900 * Copyright(c) 2006-2007, Ext JS, LLC.
11902 * Originally Released Under LGPL - original licence link has changed is not relivant.
11905 * <script type="text/javascript">
11909 Roo.data.Field = function(config){
11910 if(typeof config == "string"){
11911 config = {name: config};
11913 Roo.apply(this, config);
11916 this.type = "auto";
11919 var st = Roo.data.SortTypes;
11920 // named sortTypes are supported, here we look them up
11921 if(typeof this.sortType == "string"){
11922 this.sortType = st[this.sortType];
11925 // set default sortType for strings and dates
11926 if(!this.sortType){
11929 this.sortType = st.asUCString;
11932 this.sortType = st.asDate;
11935 this.sortType = st.none;
11940 var stripRe = /[\$,%]/g;
11942 // prebuilt conversion function for this field, instead of
11943 // switching every time we're reading a value
11945 var cv, dateFormat = this.dateFormat;
11950 cv = function(v){ return v; };
11953 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11957 return v !== undefined && v !== null && v !== '' ?
11958 parseInt(String(v).replace(stripRe, ""), 10) : '';
11963 return v !== undefined && v !== null && v !== '' ?
11964 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11969 cv = function(v){ return v === true || v === "true" || v == 1; };
11976 if(v instanceof Date){
11980 if(dateFormat == "timestamp"){
11981 return new Date(v*1000);
11983 return Date.parseDate(v, dateFormat);
11985 var parsed = Date.parse(v);
11986 return parsed ? new Date(parsed) : null;
11995 Roo.data.Field.prototype = {
12003 * Ext JS Library 1.1.1
12004 * Copyright(c) 2006-2007, Ext JS, LLC.
12006 * Originally Released Under LGPL - original licence link has changed is not relivant.
12009 * <script type="text/javascript">
12012 // Base class for reading structured data from a data source. This class is intended to be
12013 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12016 * @class Roo.data.DataReader
12017 * Base class for reading structured data from a data source. This class is intended to be
12018 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12021 Roo.data.DataReader = function(meta, recordType){
12025 this.recordType = recordType instanceof Array ?
12026 Roo.data.Record.create(recordType) : recordType;
12029 Roo.data.DataReader.prototype = {
12031 * Create an empty record
12032 * @param {Object} data (optional) - overlay some values
12033 * @return {Roo.data.Record} record created.
12035 newRow : function(d) {
12037 this.recordType.prototype.fields.each(function(c) {
12039 case 'int' : da[c.name] = 0; break;
12040 case 'date' : da[c.name] = new Date(); break;
12041 case 'float' : da[c.name] = 0.0; break;
12042 case 'boolean' : da[c.name] = false; break;
12043 default : da[c.name] = ""; break;
12047 return new this.recordType(Roo.apply(da, d));
12052 * Ext JS Library 1.1.1
12053 * Copyright(c) 2006-2007, Ext JS, LLC.
12055 * Originally Released Under LGPL - original licence link has changed is not relivant.
12058 * <script type="text/javascript">
12062 * @class Roo.data.DataProxy
12063 * @extends Roo.data.Observable
12064 * This class is an abstract base class for implementations which provide retrieval of
12065 * unformatted data objects.<br>
12067 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12068 * (of the appropriate type which knows how to parse the data object) to provide a block of
12069 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12071 * Custom implementations must implement the load method as described in
12072 * {@link Roo.data.HttpProxy#load}.
12074 Roo.data.DataProxy = function(){
12077 * @event beforeload
12078 * Fires before a network request is made to retrieve a data object.
12079 * @param {Object} This DataProxy object.
12080 * @param {Object} params The params parameter to the load function.
12085 * Fires before the load method's callback is called.
12086 * @param {Object} This DataProxy object.
12087 * @param {Object} o The data object.
12088 * @param {Object} arg The callback argument object passed to the load function.
12092 * @event loadexception
12093 * Fires if an Exception occurs during data retrieval.
12094 * @param {Object} This DataProxy object.
12095 * @param {Object} o The data object.
12096 * @param {Object} arg The callback argument object passed to the load function.
12097 * @param {Object} e The Exception.
12099 loadexception : true
12101 Roo.data.DataProxy.superclass.constructor.call(this);
12104 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12107 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12111 * Ext JS Library 1.1.1
12112 * Copyright(c) 2006-2007, Ext JS, LLC.
12114 * Originally Released Under LGPL - original licence link has changed is not relivant.
12117 * <script type="text/javascript">
12120 * @class Roo.data.MemoryProxy
12121 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12122 * to the Reader when its load method is called.
12124 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12126 Roo.data.MemoryProxy = function(data){
12130 Roo.data.MemoryProxy.superclass.constructor.call(this);
12134 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12137 * Load data from the requested source (in this case an in-memory
12138 * data object passed to the constructor), read the data object into
12139 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12140 * process that block using the passed callback.
12141 * @param {Object} params This parameter is not used by the MemoryProxy class.
12142 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12143 * object into a block of Roo.data.Records.
12144 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12145 * The function must be passed <ul>
12146 * <li>The Record block object</li>
12147 * <li>The "arg" argument from the load function</li>
12148 * <li>A boolean success indicator</li>
12150 * @param {Object} scope The scope in which to call the callback
12151 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12153 load : function(params, reader, callback, scope, arg){
12154 params = params || {};
12157 result = reader.readRecords(this.data);
12159 this.fireEvent("loadexception", this, arg, null, e);
12160 callback.call(scope, null, arg, false);
12163 callback.call(scope, result, arg, true);
12167 update : function(params, records){
12172 * Ext JS Library 1.1.1
12173 * Copyright(c) 2006-2007, Ext JS, LLC.
12175 * Originally Released Under LGPL - original licence link has changed is not relivant.
12178 * <script type="text/javascript">
12181 * @class Roo.data.HttpProxy
12182 * @extends Roo.data.DataProxy
12183 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12184 * configured to reference a certain URL.<br><br>
12186 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12187 * from which the running page was served.<br><br>
12189 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12191 * Be aware that to enable the browser to parse an XML document, the server must set
12192 * the Content-Type header in the HTTP response to "text/xml".
12194 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12195 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12196 * will be used to make the request.
12198 Roo.data.HttpProxy = function(conn){
12199 Roo.data.HttpProxy.superclass.constructor.call(this);
12200 // is conn a conn config or a real conn?
12202 this.useAjax = !conn || !conn.events;
12206 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12207 // thse are take from connection...
12210 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12213 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12214 * extra parameters to each request made by this object. (defaults to undefined)
12217 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12218 * to each request made by this object. (defaults to undefined)
12221 * @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)
12224 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12227 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12233 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12237 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12238 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12239 * a finer-grained basis than the DataProxy events.
12241 getConnection : function(){
12242 return this.useAjax ? Roo.Ajax : this.conn;
12246 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12247 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12248 * process that block using the passed callback.
12249 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12250 * for the request to the remote server.
12251 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12252 * object into a block of Roo.data.Records.
12253 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12254 * The function must be passed <ul>
12255 * <li>The Record block object</li>
12256 * <li>The "arg" argument from the load function</li>
12257 * <li>A boolean success indicator</li>
12259 * @param {Object} scope The scope in which to call the callback
12260 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12262 load : function(params, reader, callback, scope, arg){
12263 if(this.fireEvent("beforeload", this, params) !== false){
12265 params : params || {},
12267 callback : callback,
12272 callback : this.loadResponse,
12276 Roo.applyIf(o, this.conn);
12277 if(this.activeRequest){
12278 Roo.Ajax.abort(this.activeRequest);
12280 this.activeRequest = Roo.Ajax.request(o);
12282 this.conn.request(o);
12285 callback.call(scope||this, null, arg, false);
12290 loadResponse : function(o, success, response){
12291 delete this.activeRequest;
12293 this.fireEvent("loadexception", this, o, response);
12294 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12299 result = o.reader.read(response);
12301 this.fireEvent("loadexception", this, o, response, e);
12302 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12306 this.fireEvent("load", this, o, o.request.arg);
12307 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12311 update : function(dataSet){
12316 updateResponse : function(dataSet){
12321 * Ext JS Library 1.1.1
12322 * Copyright(c) 2006-2007, Ext JS, LLC.
12324 * Originally Released Under LGPL - original licence link has changed is not relivant.
12327 * <script type="text/javascript">
12331 * @class Roo.data.ScriptTagProxy
12332 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12333 * other than the originating domain of the running page.<br><br>
12335 * <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
12336 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12338 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12339 * source code that is used as the source inside a <script> tag.<br><br>
12341 * In order for the browser to process the returned data, the server must wrap the data object
12342 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12343 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12344 * depending on whether the callback name was passed:
12347 boolean scriptTag = false;
12348 String cb = request.getParameter("callback");
12351 response.setContentType("text/javascript");
12353 response.setContentType("application/x-json");
12355 Writer out = response.getWriter();
12357 out.write(cb + "(");
12359 out.print(dataBlock.toJsonString());
12366 * @param {Object} config A configuration object.
12368 Roo.data.ScriptTagProxy = function(config){
12369 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12370 Roo.apply(this, config);
12371 this.head = document.getElementsByTagName("head")[0];
12374 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12376 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12378 * @cfg {String} url The URL from which to request the data object.
12381 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12385 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12386 * the server the name of the callback function set up by the load call to process the returned data object.
12387 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12388 * javascript output which calls this named function passing the data object as its only parameter.
12390 callbackParam : "callback",
12392 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12393 * name to the request.
12398 * Load data from the configured URL, read the data object into
12399 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12400 * process that block using the passed callback.
12401 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12402 * for the request to the remote server.
12403 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12404 * object into a block of Roo.data.Records.
12405 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12406 * The function must be passed <ul>
12407 * <li>The Record block object</li>
12408 * <li>The "arg" argument from the load function</li>
12409 * <li>A boolean success indicator</li>
12411 * @param {Object} scope The scope in which to call the callback
12412 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12414 load : function(params, reader, callback, scope, arg){
12415 if(this.fireEvent("beforeload", this, params) !== false){
12417 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12419 var url = this.url;
12420 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12422 url += "&_dc=" + (new Date().getTime());
12424 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12427 cb : "stcCallback"+transId,
12428 scriptId : "stcScript"+transId,
12432 callback : callback,
12438 window[trans.cb] = function(o){
12439 conn.handleResponse(o, trans);
12442 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12444 if(this.autoAbort !== false){
12448 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12450 var script = document.createElement("script");
12451 script.setAttribute("src", url);
12452 script.setAttribute("type", "text/javascript");
12453 script.setAttribute("id", trans.scriptId);
12454 this.head.appendChild(script);
12456 this.trans = trans;
12458 callback.call(scope||this, null, arg, false);
12463 isLoading : function(){
12464 return this.trans ? true : false;
12468 * Abort the current server request.
12470 abort : function(){
12471 if(this.isLoading()){
12472 this.destroyTrans(this.trans);
12477 destroyTrans : function(trans, isLoaded){
12478 this.head.removeChild(document.getElementById(trans.scriptId));
12479 clearTimeout(trans.timeoutId);
12481 window[trans.cb] = undefined;
12483 delete window[trans.cb];
12486 // if hasn't been loaded, wait for load to remove it to prevent script error
12487 window[trans.cb] = function(){
12488 window[trans.cb] = undefined;
12490 delete window[trans.cb];
12497 handleResponse : function(o, trans){
12498 this.trans = false;
12499 this.destroyTrans(trans, true);
12502 result = trans.reader.readRecords(o);
12504 this.fireEvent("loadexception", this, o, trans.arg, e);
12505 trans.callback.call(trans.scope||window, null, trans.arg, false);
12508 this.fireEvent("load", this, o, trans.arg);
12509 trans.callback.call(trans.scope||window, result, trans.arg, true);
12513 handleFailure : function(trans){
12514 this.trans = false;
12515 this.destroyTrans(trans, false);
12516 this.fireEvent("loadexception", this, null, trans.arg);
12517 trans.callback.call(trans.scope||window, null, trans.arg, false);
12521 * Ext JS Library 1.1.1
12522 * Copyright(c) 2006-2007, Ext JS, LLC.
12524 * Originally Released Under LGPL - original licence link has changed is not relivant.
12527 * <script type="text/javascript">
12531 * @class Roo.data.JsonReader
12532 * @extends Roo.data.DataReader
12533 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12534 * based on mappings in a provided Roo.data.Record constructor.
12536 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12537 * in the reply previously.
12542 var RecordDef = Roo.data.Record.create([
12543 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12544 {name: 'occupation'} // This field will use "occupation" as the mapping.
12546 var myReader = new Roo.data.JsonReader({
12547 totalProperty: "results", // The property which contains the total dataset size (optional)
12548 root: "rows", // The property which contains an Array of row objects
12549 id: "id" // The property within each row object that provides an ID for the record (optional)
12553 * This would consume a JSON file like this:
12555 { 'results': 2, 'rows': [
12556 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12557 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12560 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12561 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12562 * paged from the remote server.
12563 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12564 * @cfg {String} root name of the property which contains the Array of row objects.
12565 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12566 * @cfg {Array} fields Array of field definition objects
12568 * Create a new JsonReader
12569 * @param {Object} meta Metadata configuration options
12570 * @param {Object} recordType Either an Array of field definition objects,
12571 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12573 Roo.data.JsonReader = function(meta, recordType){
12576 // set some defaults:
12577 Roo.applyIf(meta, {
12578 totalProperty: 'total',
12579 successProperty : 'success',
12584 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12586 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12589 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12590 * Used by Store query builder to append _requestMeta to params.
12593 metaFromRemote : false,
12595 * This method is only used by a DataProxy which has retrieved data from a remote server.
12596 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12597 * @return {Object} data A data block which is used by an Roo.data.Store object as
12598 * a cache of Roo.data.Records.
12600 read : function(response){
12601 var json = response.responseText;
12603 var o = /* eval:var:o */ eval("("+json+")");
12605 throw {message: "JsonReader.read: Json object not found"};
12611 this.metaFromRemote = true;
12612 this.meta = o.metaData;
12613 this.recordType = Roo.data.Record.create(o.metaData.fields);
12614 this.onMetaChange(this.meta, this.recordType, o);
12616 return this.readRecords(o);
12619 // private function a store will implement
12620 onMetaChange : function(meta, recordType, o){
12627 simpleAccess: function(obj, subsc) {
12634 getJsonAccessor: function(){
12636 return function(expr) {
12638 return(re.test(expr))
12639 ? new Function("obj", "return obj." + expr)
12644 return Roo.emptyFn;
12649 * Create a data block containing Roo.data.Records from an XML document.
12650 * @param {Object} o An object which contains an Array of row objects in the property specified
12651 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12652 * which contains the total size of the dataset.
12653 * @return {Object} data A data block which is used by an Roo.data.Store object as
12654 * a cache of Roo.data.Records.
12656 readRecords : function(o){
12658 * After any data loads, the raw JSON data is available for further custom processing.
12662 var s = this.meta, Record = this.recordType,
12663 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12665 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12667 if(s.totalProperty) {
12668 this.getTotal = this.getJsonAccessor(s.totalProperty);
12670 if(s.successProperty) {
12671 this.getSuccess = this.getJsonAccessor(s.successProperty);
12673 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12675 var g = this.getJsonAccessor(s.id);
12676 this.getId = function(rec) {
12678 return (r === undefined || r === "") ? null : r;
12681 this.getId = function(){return null;};
12684 for(var jj = 0; jj < fl; jj++){
12686 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12687 this.ef[jj] = this.getJsonAccessor(map);
12691 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12692 if(s.totalProperty){
12693 var vt = parseInt(this.getTotal(o), 10);
12698 if(s.successProperty){
12699 var vs = this.getSuccess(o);
12700 if(vs === false || vs === 'false'){
12705 for(var i = 0; i < c; i++){
12708 var id = this.getId(n);
12709 for(var j = 0; j < fl; j++){
12711 var v = this.ef[j](n);
12713 Roo.log('missing convert for ' + f.name);
12717 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12719 var record = new Record(values, id);
12721 records[i] = record;
12727 totalRecords : totalRecords
12732 * Ext JS Library 1.1.1
12733 * Copyright(c) 2006-2007, Ext JS, LLC.
12735 * Originally Released Under LGPL - original licence link has changed is not relivant.
12738 * <script type="text/javascript">
12742 * @class Roo.data.ArrayReader
12743 * @extends Roo.data.DataReader
12744 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12745 * Each element of that Array represents a row of data fields. The
12746 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12747 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12751 var RecordDef = Roo.data.Record.create([
12752 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12753 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12755 var myReader = new Roo.data.ArrayReader({
12756 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12760 * This would consume an Array like this:
12762 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12764 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12766 * Create a new JsonReader
12767 * @param {Object} meta Metadata configuration options.
12768 * @param {Object} recordType Either an Array of field definition objects
12769 * as specified to {@link Roo.data.Record#create},
12770 * or an {@link Roo.data.Record} object
12771 * created using {@link Roo.data.Record#create}.
12773 Roo.data.ArrayReader = function(meta, recordType){
12774 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12777 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12779 * Create a data block containing Roo.data.Records from an XML document.
12780 * @param {Object} o An Array of row objects which represents the dataset.
12781 * @return {Object} data A data block which is used by an Roo.data.Store object as
12782 * a cache of Roo.data.Records.
12784 readRecords : function(o){
12785 var sid = this.meta ? this.meta.id : null;
12786 var recordType = this.recordType, fields = recordType.prototype.fields;
12789 for(var i = 0; i < root.length; i++){
12792 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12793 for(var j = 0, jlen = fields.length; j < jlen; j++){
12794 var f = fields.items[j];
12795 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12796 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12798 values[f.name] = v;
12800 var record = new recordType(values, id);
12802 records[records.length] = record;
12806 totalRecords : records.length
12815 * @class Roo.bootstrap.ComboBox
12816 * @extends Roo.bootstrap.TriggerField
12817 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12818 * @cfg {Boolean} append (true|false) default false
12819 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12820 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12821 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12822 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12823 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12824 * @cfg {Boolean} animate default true
12825 * @cfg {Boolean} emptyResultText only for touch device
12826 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12827 * @cfg {String} emptyTitle default ''
12829 * Create a new ComboBox.
12830 * @param {Object} config Configuration options
12832 Roo.bootstrap.ComboBox = function(config){
12833 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12837 * Fires when the dropdown list is expanded
12838 * @param {Roo.bootstrap.ComboBox} combo This combo box
12843 * Fires when the dropdown list is collapsed
12844 * @param {Roo.bootstrap.ComboBox} combo This combo box
12848 * @event beforeselect
12849 * Fires before a list item is selected. Return false to cancel the selection.
12850 * @param {Roo.bootstrap.ComboBox} combo This combo box
12851 * @param {Roo.data.Record} record The data record returned from the underlying store
12852 * @param {Number} index The index of the selected item in the dropdown list
12854 'beforeselect' : true,
12857 * Fires when a list item is selected
12858 * @param {Roo.bootstrap.ComboBox} combo This combo box
12859 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12860 * @param {Number} index The index of the selected item in the dropdown list
12864 * @event beforequery
12865 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12866 * The event object passed has these properties:
12867 * @param {Roo.bootstrap.ComboBox} combo This combo box
12868 * @param {String} query The query
12869 * @param {Boolean} forceAll true to force "all" query
12870 * @param {Boolean} cancel true to cancel the query
12871 * @param {Object} e The query event object
12873 'beforequery': true,
12876 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12877 * @param {Roo.bootstrap.ComboBox} combo This combo box
12882 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12883 * @param {Roo.bootstrap.ComboBox} combo This combo box
12884 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12889 * Fires when the remove value from the combobox array
12890 * @param {Roo.bootstrap.ComboBox} combo This combo box
12894 * @event afterremove
12895 * Fires when the remove value from the combobox array
12896 * @param {Roo.bootstrap.ComboBox} combo This combo box
12898 'afterremove' : true,
12900 * @event specialfilter
12901 * Fires when specialfilter
12902 * @param {Roo.bootstrap.ComboBox} combo This combo box
12904 'specialfilter' : true,
12907 * Fires when tick the element
12908 * @param {Roo.bootstrap.ComboBox} combo This combo box
12912 * @event touchviewdisplay
12913 * Fires when touch view require special display (default is using displayField)
12914 * @param {Roo.bootstrap.ComboBox} combo This combo box
12915 * @param {Object} cfg set html .
12917 'touchviewdisplay' : true
12922 this.tickItems = [];
12924 this.selectedIndex = -1;
12925 if(this.mode == 'local'){
12926 if(config.queryDelay === undefined){
12927 this.queryDelay = 10;
12929 if(config.minChars === undefined){
12935 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12938 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12939 * rendering into an Roo.Editor, defaults to false)
12942 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12943 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12946 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12949 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12950 * the dropdown list (defaults to undefined, with no header element)
12954 * @cfg {String/Roo.Template} tpl The template to use to render the output
12958 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12960 listWidth: undefined,
12962 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12963 * mode = 'remote' or 'text' if mode = 'local')
12965 displayField: undefined,
12968 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12969 * mode = 'remote' or 'value' if mode = 'local').
12970 * Note: use of a valueField requires the user make a selection
12971 * in order for a value to be mapped.
12973 valueField: undefined,
12975 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12980 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12981 * field's data value (defaults to the underlying DOM element's name)
12983 hiddenName: undefined,
12985 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12989 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12991 selectedClass: 'active',
12994 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12998 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12999 * anchor positions (defaults to 'tl-bl')
13001 listAlign: 'tl-bl?',
13003 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13007 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
13008 * query specified by the allQuery config option (defaults to 'query')
13010 triggerAction: 'query',
13012 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13013 * (defaults to 4, does not apply if editable = false)
13017 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13018 * delay (typeAheadDelay) if it matches a known value (defaults to false)
13022 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13023 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13027 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13028 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
13032 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
13033 * when editable = true (defaults to false)
13035 selectOnFocus:false,
13037 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13039 queryParam: 'query',
13041 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13042 * when mode = 'remote' (defaults to 'Loading...')
13044 loadingText: 'Loading...',
13046 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13050 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13054 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13055 * traditional select (defaults to true)
13059 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13063 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13067 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13068 * listWidth has a higher value)
13072 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13073 * allow the user to set arbitrary text into the field (defaults to false)
13075 forceSelection:false,
13077 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13078 * if typeAhead = true (defaults to 250)
13080 typeAheadDelay : 250,
13082 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13083 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13085 valueNotFoundText : undefined,
13087 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13089 blockFocus : false,
13092 * @cfg {Boolean} disableClear Disable showing of clear button.
13094 disableClear : false,
13096 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13098 alwaysQuery : false,
13101 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13106 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13108 invalidClass : "has-warning",
13111 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13113 validClass : "has-success",
13116 * @cfg {Boolean} specialFilter (true|false) special filter default false
13118 specialFilter : false,
13121 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13123 mobileTouchView : true,
13126 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13128 useNativeIOS : false,
13131 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13133 mobile_restrict_height : false,
13135 ios_options : false,
13147 btnPosition : 'right',
13148 triggerList : true,
13149 showToggleBtn : true,
13151 emptyResultText: 'Empty',
13152 triggerText : 'Select',
13155 // element that contains real text value.. (when hidden is used..)
13157 getAutoCreate : function()
13162 * Render classic select for iso
13165 if(Roo.isIOS && this.useNativeIOS){
13166 cfg = this.getAutoCreateNativeIOS();
13174 if(Roo.isTouch && this.mobileTouchView){
13175 cfg = this.getAutoCreateTouchView();
13182 if(!this.tickable){
13183 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13188 * ComboBox with tickable selections
13191 var align = this.labelAlign || this.parentLabelAlign();
13194 cls : 'form-group roo-combobox-tickable' //input-group
13197 var btn_text_select = '';
13198 var btn_text_done = '';
13199 var btn_text_cancel = '';
13201 if (this.btn_text_show) {
13202 btn_text_select = 'Select';
13203 btn_text_done = 'Done';
13204 btn_text_cancel = 'Cancel';
13209 cls : 'tickable-buttons',
13214 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13215 //html : this.triggerText
13216 html: btn_text_select
13222 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13224 html: btn_text_done
13230 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13232 html: btn_text_cancel
13238 buttons.cn.unshift({
13240 cls: 'roo-select2-search-field-input'
13246 Roo.each(buttons.cn, function(c){
13248 c.cls += ' btn-' + _this.size;
13251 if (_this.disabled) {
13262 cls: 'form-hidden-field'
13266 cls: 'roo-select2-choices',
13270 cls: 'roo-select2-search-field',
13281 cls: 'roo-select2-container input-group roo-select2-container-multi',
13286 // cls: 'typeahead typeahead-long dropdown-menu',
13287 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13292 if(this.hasFeedback && !this.allowBlank){
13296 cls: 'glyphicon form-control-feedback'
13299 combobox.cn.push(feedback);
13303 if (align ==='left' && this.fieldLabel.length) {
13305 cfg.cls += ' roo-form-group-label-left';
13310 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13311 tooltip : 'This field is required'
13316 cls : 'control-label',
13317 html : this.fieldLabel
13329 var labelCfg = cfg.cn[1];
13330 var contentCfg = cfg.cn[2];
13333 if(this.indicatorpos == 'right'){
13339 cls : 'control-label',
13343 html : this.fieldLabel
13347 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13348 tooltip : 'This field is required'
13363 labelCfg = cfg.cn[0];
13364 contentCfg = cfg.cn[1];
13368 if(this.labelWidth > 12){
13369 labelCfg.style = "width: " + this.labelWidth + 'px';
13372 if(this.labelWidth < 13 && this.labelmd == 0){
13373 this.labelmd = this.labelWidth;
13376 if(this.labellg > 0){
13377 labelCfg.cls += ' col-lg-' + this.labellg;
13378 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13381 if(this.labelmd > 0){
13382 labelCfg.cls += ' col-md-' + this.labelmd;
13383 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13386 if(this.labelsm > 0){
13387 labelCfg.cls += ' col-sm-' + this.labelsm;
13388 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13391 if(this.labelxs > 0){
13392 labelCfg.cls += ' col-xs-' + this.labelxs;
13393 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13397 } else if ( this.fieldLabel.length) {
13398 // Roo.log(" label");
13402 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13403 tooltip : 'This field is required'
13407 //cls : 'input-group-addon',
13408 html : this.fieldLabel
13413 if(this.indicatorpos == 'right'){
13417 //cls : 'input-group-addon',
13418 html : this.fieldLabel
13422 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13423 tooltip : 'This field is required'
13432 // Roo.log(" no label && no align");
13439 ['xs','sm','md','lg'].map(function(size){
13440 if (settings[size]) {
13441 cfg.cls += ' col-' + size + '-' + settings[size];
13449 _initEventsCalled : false,
13452 initEvents: function()
13454 if (this._initEventsCalled) { // as we call render... prevent looping...
13457 this._initEventsCalled = true;
13460 throw "can not find store for combo";
13463 this.indicator = this.indicatorEl();
13465 this.store = Roo.factory(this.store, Roo.data);
13466 this.store.parent = this;
13468 // if we are building from html. then this element is so complex, that we can not really
13469 // use the rendered HTML.
13470 // so we have to trash and replace the previous code.
13471 if (Roo.XComponent.build_from_html) {
13472 // remove this element....
13473 var e = this.el.dom, k=0;
13474 while (e ) { e = e.previousSibling; ++k;}
13479 this.rendered = false;
13481 this.render(this.parent().getChildContainer(true), k);
13484 if(Roo.isIOS && this.useNativeIOS){
13485 this.initIOSView();
13493 if(Roo.isTouch && this.mobileTouchView){
13494 this.initTouchView();
13499 this.initTickableEvents();
13503 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13505 if(this.hiddenName){
13507 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13509 this.hiddenField.dom.value =
13510 this.hiddenValue !== undefined ? this.hiddenValue :
13511 this.value !== undefined ? this.value : '';
13513 // prevent input submission
13514 this.el.dom.removeAttribute('name');
13515 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13520 // this.el.dom.setAttribute('autocomplete', 'off');
13523 var cls = 'x-combo-list';
13525 //this.list = new Roo.Layer({
13526 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13532 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13533 _this.list.setWidth(lw);
13536 this.list.on('mouseover', this.onViewOver, this);
13537 this.list.on('mousemove', this.onViewMove, this);
13538 this.list.on('scroll', this.onViewScroll, this);
13541 this.list.swallowEvent('mousewheel');
13542 this.assetHeight = 0;
13545 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13546 this.assetHeight += this.header.getHeight();
13549 this.innerList = this.list.createChild({cls:cls+'-inner'});
13550 this.innerList.on('mouseover', this.onViewOver, this);
13551 this.innerList.on('mousemove', this.onViewMove, this);
13552 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13554 if(this.allowBlank && !this.pageSize && !this.disableClear){
13555 this.footer = this.list.createChild({cls:cls+'-ft'});
13556 this.pageTb = new Roo.Toolbar(this.footer);
13560 this.footer = this.list.createChild({cls:cls+'-ft'});
13561 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13562 {pageSize: this.pageSize});
13566 if (this.pageTb && this.allowBlank && !this.disableClear) {
13568 this.pageTb.add(new Roo.Toolbar.Fill(), {
13569 cls: 'x-btn-icon x-btn-clear',
13571 handler: function()
13574 _this.clearValue();
13575 _this.onSelect(false, -1);
13580 this.assetHeight += this.footer.getHeight();
13585 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13588 this.view = new Roo.View(this.list, this.tpl, {
13589 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13591 //this.view.wrapEl.setDisplayed(false);
13592 this.view.on('click', this.onViewClick, this);
13595 this.store.on('beforeload', this.onBeforeLoad, this);
13596 this.store.on('load', this.onLoad, this);
13597 this.store.on('loadexception', this.onLoadException, this);
13599 if(this.resizable){
13600 this.resizer = new Roo.Resizable(this.list, {
13601 pinned:true, handles:'se'
13603 this.resizer.on('resize', function(r, w, h){
13604 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13605 this.listWidth = w;
13606 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13607 this.restrictHeight();
13609 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13612 if(!this.editable){
13613 this.editable = true;
13614 this.setEditable(false);
13619 if (typeof(this.events.add.listeners) != 'undefined') {
13621 this.addicon = this.wrap.createChild(
13622 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13624 this.addicon.on('click', function(e) {
13625 this.fireEvent('add', this);
13628 if (typeof(this.events.edit.listeners) != 'undefined') {
13630 this.editicon = this.wrap.createChild(
13631 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13632 if (this.addicon) {
13633 this.editicon.setStyle('margin-left', '40px');
13635 this.editicon.on('click', function(e) {
13637 // we fire even if inothing is selected..
13638 this.fireEvent('edit', this, this.lastData );
13644 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13645 "up" : function(e){
13646 this.inKeyMode = true;
13650 "down" : function(e){
13651 if(!this.isExpanded()){
13652 this.onTriggerClick();
13654 this.inKeyMode = true;
13659 "enter" : function(e){
13660 // this.onViewClick();
13664 if(this.fireEvent("specialkey", this, e)){
13665 this.onViewClick(false);
13671 "esc" : function(e){
13675 "tab" : function(e){
13678 if(this.fireEvent("specialkey", this, e)){
13679 this.onViewClick(false);
13687 doRelay : function(foo, bar, hname){
13688 if(hname == 'down' || this.scope.isExpanded()){
13689 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13698 this.queryDelay = Math.max(this.queryDelay || 10,
13699 this.mode == 'local' ? 10 : 250);
13702 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13704 if(this.typeAhead){
13705 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13707 if(this.editable !== false){
13708 this.inputEl().on("keyup", this.onKeyUp, this);
13710 if(this.forceSelection){
13711 this.inputEl().on('blur', this.doForce, this);
13715 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13716 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13720 initTickableEvents: function()
13724 if(this.hiddenName){
13726 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13728 this.hiddenField.dom.value =
13729 this.hiddenValue !== undefined ? this.hiddenValue :
13730 this.value !== undefined ? this.value : '';
13732 // prevent input submission
13733 this.el.dom.removeAttribute('name');
13734 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13739 // this.list = this.el.select('ul.dropdown-menu',true).first();
13741 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13742 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13743 if(this.triggerList){
13744 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13747 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13748 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13750 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13751 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13753 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13754 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13756 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13757 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13758 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13761 this.cancelBtn.hide();
13766 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13767 _this.list.setWidth(lw);
13770 this.list.on('mouseover', this.onViewOver, this);
13771 this.list.on('mousemove', this.onViewMove, this);
13773 this.list.on('scroll', this.onViewScroll, this);
13776 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13777 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13780 this.view = new Roo.View(this.list, this.tpl, {
13785 selectedClass: this.selectedClass
13788 //this.view.wrapEl.setDisplayed(false);
13789 this.view.on('click', this.onViewClick, this);
13793 this.store.on('beforeload', this.onBeforeLoad, this);
13794 this.store.on('load', this.onLoad, this);
13795 this.store.on('loadexception', this.onLoadException, this);
13798 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13799 "up" : function(e){
13800 this.inKeyMode = true;
13804 "down" : function(e){
13805 this.inKeyMode = true;
13809 "enter" : function(e){
13810 if(this.fireEvent("specialkey", this, e)){
13811 this.onViewClick(false);
13817 "esc" : function(e){
13818 this.onTickableFooterButtonClick(e, false, false);
13821 "tab" : function(e){
13822 this.fireEvent("specialkey", this, e);
13824 this.onTickableFooterButtonClick(e, false, false);
13831 doRelay : function(e, fn, key){
13832 if(this.scope.isExpanded()){
13833 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13842 this.queryDelay = Math.max(this.queryDelay || 10,
13843 this.mode == 'local' ? 10 : 250);
13846 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13848 if(this.typeAhead){
13849 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13852 if(this.editable !== false){
13853 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13856 this.indicator = this.indicatorEl();
13858 if(this.indicator){
13859 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13860 this.indicator.hide();
13865 onDestroy : function(){
13867 this.view.setStore(null);
13868 this.view.el.removeAllListeners();
13869 this.view.el.remove();
13870 this.view.purgeListeners();
13873 this.list.dom.innerHTML = '';
13877 this.store.un('beforeload', this.onBeforeLoad, this);
13878 this.store.un('load', this.onLoad, this);
13879 this.store.un('loadexception', this.onLoadException, this);
13881 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13885 fireKey : function(e){
13886 if(e.isNavKeyPress() && !this.list.isVisible()){
13887 this.fireEvent("specialkey", this, e);
13892 onResize: function(w, h){
13893 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13895 // if(typeof w != 'number'){
13896 // // we do not handle it!?!?
13899 // var tw = this.trigger.getWidth();
13900 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13901 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13903 // this.inputEl().setWidth( this.adjustWidth('input', x));
13905 // //this.trigger.setStyle('left', x+'px');
13907 // if(this.list && this.listWidth === undefined){
13908 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13909 // this.list.setWidth(lw);
13910 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13918 * Allow or prevent the user from directly editing the field text. If false is passed,
13919 * the user will only be able to select from the items defined in the dropdown list. This method
13920 * is the runtime equivalent of setting the 'editable' config option at config time.
13921 * @param {Boolean} value True to allow the user to directly edit the field text
13923 setEditable : function(value){
13924 if(value == this.editable){
13927 this.editable = value;
13929 this.inputEl().dom.setAttribute('readOnly', true);
13930 this.inputEl().on('mousedown', this.onTriggerClick, this);
13931 this.inputEl().addClass('x-combo-noedit');
13933 this.inputEl().dom.setAttribute('readOnly', false);
13934 this.inputEl().un('mousedown', this.onTriggerClick, this);
13935 this.inputEl().removeClass('x-combo-noedit');
13941 onBeforeLoad : function(combo,opts){
13942 if(!this.hasFocus){
13946 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13948 this.restrictHeight();
13949 this.selectedIndex = -1;
13953 onLoad : function(){
13955 this.hasQuery = false;
13957 if(!this.hasFocus){
13961 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13962 this.loading.hide();
13965 if(this.store.getCount() > 0){
13968 this.restrictHeight();
13969 if(this.lastQuery == this.allQuery){
13970 if(this.editable && !this.tickable){
13971 this.inputEl().dom.select();
13975 !this.selectByValue(this.value, true) &&
13978 !this.store.lastOptions ||
13979 typeof(this.store.lastOptions.add) == 'undefined' ||
13980 this.store.lastOptions.add != true
13983 this.select(0, true);
13986 if(this.autoFocus){
13989 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13990 this.taTask.delay(this.typeAheadDelay);
13994 this.onEmptyResults();
14000 onLoadException : function()
14002 this.hasQuery = false;
14004 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14005 this.loading.hide();
14008 if(this.tickable && this.editable){
14013 // only causes errors at present
14014 //Roo.log(this.store.reader.jsonData);
14015 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14017 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14023 onTypeAhead : function(){
14024 if(this.store.getCount() > 0){
14025 var r = this.store.getAt(0);
14026 var newValue = r.data[this.displayField];
14027 var len = newValue.length;
14028 var selStart = this.getRawValue().length;
14030 if(selStart != len){
14031 this.setRawValue(newValue);
14032 this.selectText(selStart, newValue.length);
14038 onSelect : function(record, index){
14040 if(this.fireEvent('beforeselect', this, record, index) !== false){
14042 this.setFromData(index > -1 ? record.data : false);
14045 this.fireEvent('select', this, record, index);
14050 * Returns the currently selected field value or empty string if no value is set.
14051 * @return {String} value The selected value
14053 getValue : function()
14055 if(Roo.isIOS && this.useNativeIOS){
14056 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14060 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14063 if(this.valueField){
14064 return typeof this.value != 'undefined' ? this.value : '';
14066 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14070 getRawValue : function()
14072 if(Roo.isIOS && this.useNativeIOS){
14073 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14076 var v = this.inputEl().getValue();
14082 * Clears any text/value currently set in the field
14084 clearValue : function(){
14086 if(this.hiddenField){
14087 this.hiddenField.dom.value = '';
14090 this.setRawValue('');
14091 this.lastSelectionText = '';
14092 this.lastData = false;
14094 var close = this.closeTriggerEl();
14105 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14106 * will be displayed in the field. If the value does not match the data value of an existing item,
14107 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14108 * Otherwise the field will be blank (although the value will still be set).
14109 * @param {String} value The value to match
14111 setValue : function(v)
14113 if(Roo.isIOS && this.useNativeIOS){
14114 this.setIOSValue(v);
14124 if(this.valueField){
14125 var r = this.findRecord(this.valueField, v);
14127 text = r.data[this.displayField];
14128 }else if(this.valueNotFoundText !== undefined){
14129 text = this.valueNotFoundText;
14132 this.lastSelectionText = text;
14133 if(this.hiddenField){
14134 this.hiddenField.dom.value = v;
14136 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14139 var close = this.closeTriggerEl();
14142 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14148 * @property {Object} the last set data for the element
14153 * Sets the value of the field based on a object which is related to the record format for the store.
14154 * @param {Object} value the value to set as. or false on reset?
14156 setFromData : function(o){
14163 var dv = ''; // display value
14164 var vv = ''; // value value..
14166 if (this.displayField) {
14167 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14169 // this is an error condition!!!
14170 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14173 if(this.valueField){
14174 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14177 var close = this.closeTriggerEl();
14180 if(dv.length || vv * 1 > 0){
14182 this.blockFocus=true;
14188 if(this.hiddenField){
14189 this.hiddenField.dom.value = vv;
14191 this.lastSelectionText = dv;
14192 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14196 // no hidden field.. - we store the value in 'value', but still display
14197 // display field!!!!
14198 this.lastSelectionText = dv;
14199 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14206 reset : function(){
14207 // overridden so that last data is reset..
14214 this.setValue(this.originalValue);
14215 //this.clearInvalid();
14216 this.lastData = false;
14218 this.view.clearSelections();
14224 findRecord : function(prop, value){
14226 if(this.store.getCount() > 0){
14227 this.store.each(function(r){
14228 if(r.data[prop] == value){
14238 getName: function()
14240 // returns hidden if it's set..
14241 if (!this.rendered) {return ''};
14242 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14246 onViewMove : function(e, t){
14247 this.inKeyMode = false;
14251 onViewOver : function(e, t){
14252 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14255 var item = this.view.findItemFromChild(t);
14258 var index = this.view.indexOf(item);
14259 this.select(index, false);
14264 onViewClick : function(view, doFocus, el, e)
14266 var index = this.view.getSelectedIndexes()[0];
14268 var r = this.store.getAt(index);
14272 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14279 Roo.each(this.tickItems, function(v,k){
14281 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14283 _this.tickItems.splice(k, 1);
14285 if(typeof(e) == 'undefined' && view == false){
14286 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14298 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14299 this.tickItems.push(r.data);
14302 if(typeof(e) == 'undefined' && view == false){
14303 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14310 this.onSelect(r, index);
14312 if(doFocus !== false && !this.blockFocus){
14313 this.inputEl().focus();
14318 restrictHeight : function(){
14319 //this.innerList.dom.style.height = '';
14320 //var inner = this.innerList.dom;
14321 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14322 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14323 //this.list.beginUpdate();
14324 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14325 this.list.alignTo(this.inputEl(), this.listAlign);
14326 this.list.alignTo(this.inputEl(), this.listAlign);
14327 //this.list.endUpdate();
14331 onEmptyResults : function(){
14333 if(this.tickable && this.editable){
14334 this.hasFocus = false;
14335 this.restrictHeight();
14343 * Returns true if the dropdown list is expanded, else false.
14345 isExpanded : function(){
14346 return this.list.isVisible();
14350 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14351 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14352 * @param {String} value The data value of the item to select
14353 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14354 * selected item if it is not currently in view (defaults to true)
14355 * @return {Boolean} True if the value matched an item in the list, else false
14357 selectByValue : function(v, scrollIntoView){
14358 if(v !== undefined && v !== null){
14359 var r = this.findRecord(this.valueField || this.displayField, v);
14361 this.select(this.store.indexOf(r), scrollIntoView);
14369 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14370 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14371 * @param {Number} index The zero-based index of the list item to select
14372 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14373 * selected item if it is not currently in view (defaults to true)
14375 select : function(index, scrollIntoView){
14376 this.selectedIndex = index;
14377 this.view.select(index);
14378 if(scrollIntoView !== false){
14379 var el = this.view.getNode(index);
14381 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14384 this.list.scrollChildIntoView(el, false);
14390 selectNext : function(){
14391 var ct = this.store.getCount();
14393 if(this.selectedIndex == -1){
14395 }else if(this.selectedIndex < ct-1){
14396 this.select(this.selectedIndex+1);
14402 selectPrev : function(){
14403 var ct = this.store.getCount();
14405 if(this.selectedIndex == -1){
14407 }else if(this.selectedIndex != 0){
14408 this.select(this.selectedIndex-1);
14414 onKeyUp : function(e){
14415 if(this.editable !== false && !e.isSpecialKey()){
14416 this.lastKey = e.getKey();
14417 this.dqTask.delay(this.queryDelay);
14422 validateBlur : function(){
14423 return !this.list || !this.list.isVisible();
14427 initQuery : function(){
14429 var v = this.getRawValue();
14431 if(this.tickable && this.editable){
14432 v = this.tickableInputEl().getValue();
14439 doForce : function(){
14440 if(this.inputEl().dom.value.length > 0){
14441 this.inputEl().dom.value =
14442 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14448 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14449 * query allowing the query action to be canceled if needed.
14450 * @param {String} query The SQL query to execute
14451 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14452 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14453 * saved in the current store (defaults to false)
14455 doQuery : function(q, forceAll){
14457 if(q === undefined || q === null){
14462 forceAll: forceAll,
14466 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14471 forceAll = qe.forceAll;
14472 if(forceAll === true || (q.length >= this.minChars)){
14474 this.hasQuery = true;
14476 if(this.lastQuery != q || this.alwaysQuery){
14477 this.lastQuery = q;
14478 if(this.mode == 'local'){
14479 this.selectedIndex = -1;
14481 this.store.clearFilter();
14484 if(this.specialFilter){
14485 this.fireEvent('specialfilter', this);
14490 this.store.filter(this.displayField, q);
14493 this.store.fireEvent("datachanged", this.store);
14500 this.store.baseParams[this.queryParam] = q;
14502 var options = {params : this.getParams(q)};
14505 options.add = true;
14506 options.params.start = this.page * this.pageSize;
14509 this.store.load(options);
14512 * this code will make the page width larger, at the beginning, the list not align correctly,
14513 * we should expand the list on onLoad
14514 * so command out it
14519 this.selectedIndex = -1;
14524 this.loadNext = false;
14528 getParams : function(q){
14530 //p[this.queryParam] = q;
14534 p.limit = this.pageSize;
14540 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14542 collapse : function(){
14543 if(!this.isExpanded()){
14549 this.hasFocus = false;
14553 this.cancelBtn.hide();
14554 this.trigger.show();
14557 this.tickableInputEl().dom.value = '';
14558 this.tickableInputEl().blur();
14563 Roo.get(document).un('mousedown', this.collapseIf, this);
14564 Roo.get(document).un('mousewheel', this.collapseIf, this);
14565 if (!this.editable) {
14566 Roo.get(document).un('keydown', this.listKeyPress, this);
14568 this.fireEvent('collapse', this);
14574 collapseIf : function(e){
14575 var in_combo = e.within(this.el);
14576 var in_list = e.within(this.list);
14577 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14579 if (in_combo || in_list || is_list) {
14580 //e.stopPropagation();
14585 this.onTickableFooterButtonClick(e, false, false);
14593 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14595 expand : function(){
14597 if(this.isExpanded() || !this.hasFocus){
14601 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14602 this.list.setWidth(lw);
14608 this.restrictHeight();
14612 this.tickItems = Roo.apply([], this.item);
14615 this.cancelBtn.show();
14616 this.trigger.hide();
14619 this.tickableInputEl().focus();
14624 Roo.get(document).on('mousedown', this.collapseIf, this);
14625 Roo.get(document).on('mousewheel', this.collapseIf, this);
14626 if (!this.editable) {
14627 Roo.get(document).on('keydown', this.listKeyPress, this);
14630 this.fireEvent('expand', this);
14634 // Implements the default empty TriggerField.onTriggerClick function
14635 onTriggerClick : function(e)
14637 Roo.log('trigger click');
14639 if(this.disabled || !this.triggerList){
14644 this.loadNext = false;
14646 if(this.isExpanded()){
14648 if (!this.blockFocus) {
14649 this.inputEl().focus();
14653 this.hasFocus = true;
14654 if(this.triggerAction == 'all') {
14655 this.doQuery(this.allQuery, true);
14657 this.doQuery(this.getRawValue());
14659 if (!this.blockFocus) {
14660 this.inputEl().focus();
14665 onTickableTriggerClick : function(e)
14672 this.loadNext = false;
14673 this.hasFocus = true;
14675 if(this.triggerAction == 'all') {
14676 this.doQuery(this.allQuery, true);
14678 this.doQuery(this.getRawValue());
14682 onSearchFieldClick : function(e)
14684 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14685 this.onTickableFooterButtonClick(e, false, false);
14689 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14694 this.loadNext = false;
14695 this.hasFocus = true;
14697 if(this.triggerAction == 'all') {
14698 this.doQuery(this.allQuery, true);
14700 this.doQuery(this.getRawValue());
14704 listKeyPress : function(e)
14706 //Roo.log('listkeypress');
14707 // scroll to first matching element based on key pres..
14708 if (e.isSpecialKey()) {
14711 var k = String.fromCharCode(e.getKey()).toUpperCase();
14714 var csel = this.view.getSelectedNodes();
14715 var cselitem = false;
14717 var ix = this.view.indexOf(csel[0]);
14718 cselitem = this.store.getAt(ix);
14719 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14725 this.store.each(function(v) {
14727 // start at existing selection.
14728 if (cselitem.id == v.id) {
14734 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14735 match = this.store.indexOf(v);
14741 if (match === false) {
14742 return true; // no more action?
14745 this.view.select(match);
14746 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14747 sn.scrollIntoView(sn.dom.parentNode, false);
14750 onViewScroll : function(e, t){
14752 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){
14756 this.hasQuery = true;
14758 this.loading = this.list.select('.loading', true).first();
14760 if(this.loading === null){
14761 this.list.createChild({
14763 cls: 'loading roo-select2-more-results roo-select2-active',
14764 html: 'Loading more results...'
14767 this.loading = this.list.select('.loading', true).first();
14769 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14771 this.loading.hide();
14774 this.loading.show();
14779 this.loadNext = true;
14781 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14786 addItem : function(o)
14788 var dv = ''; // display value
14790 if (this.displayField) {
14791 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14793 // this is an error condition!!!
14794 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14801 var choice = this.choices.createChild({
14803 cls: 'roo-select2-search-choice',
14812 cls: 'roo-select2-search-choice-close fa fa-times',
14817 }, this.searchField);
14819 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14821 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14829 this.inputEl().dom.value = '';
14834 onRemoveItem : function(e, _self, o)
14836 e.preventDefault();
14838 this.lastItem = Roo.apply([], this.item);
14840 var index = this.item.indexOf(o.data) * 1;
14843 Roo.log('not this item?!');
14847 this.item.splice(index, 1);
14852 this.fireEvent('remove', this, e);
14858 syncValue : function()
14860 if(!this.item.length){
14867 Roo.each(this.item, function(i){
14868 if(_this.valueField){
14869 value.push(i[_this.valueField]);
14876 this.value = value.join(',');
14878 if(this.hiddenField){
14879 this.hiddenField.dom.value = this.value;
14882 this.store.fireEvent("datachanged", this.store);
14887 clearItem : function()
14889 if(!this.multiple){
14895 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14903 if(this.tickable && !Roo.isTouch){
14904 this.view.refresh();
14908 inputEl: function ()
14910 if(Roo.isIOS && this.useNativeIOS){
14911 return this.el.select('select.roo-ios-select', true).first();
14914 if(Roo.isTouch && this.mobileTouchView){
14915 return this.el.select('input.form-control',true).first();
14919 return this.searchField;
14922 return this.el.select('input.form-control',true).first();
14925 onTickableFooterButtonClick : function(e, btn, el)
14927 e.preventDefault();
14929 this.lastItem = Roo.apply([], this.item);
14931 if(btn && btn.name == 'cancel'){
14932 this.tickItems = Roo.apply([], this.item);
14941 Roo.each(this.tickItems, function(o){
14949 validate : function()
14951 if(this.getVisibilityEl().hasClass('hidden')){
14955 var v = this.getRawValue();
14958 v = this.getValue();
14961 if(this.disabled || this.allowBlank || v.length){
14966 this.markInvalid();
14970 tickableInputEl : function()
14972 if(!this.tickable || !this.editable){
14973 return this.inputEl();
14976 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14980 getAutoCreateTouchView : function()
14985 cls: 'form-group' //input-group
14991 type : this.inputType,
14992 cls : 'form-control x-combo-noedit',
14993 autocomplete: 'new-password',
14994 placeholder : this.placeholder || '',
14999 input.name = this.name;
15003 input.cls += ' input-' + this.size;
15006 if (this.disabled) {
15007 input.disabled = true;
15018 inputblock.cls += ' input-group';
15020 inputblock.cn.unshift({
15022 cls : 'input-group-addon',
15027 if(this.removable && !this.multiple){
15028 inputblock.cls += ' roo-removable';
15030 inputblock.cn.push({
15033 cls : 'roo-combo-removable-btn close'
15037 if(this.hasFeedback && !this.allowBlank){
15039 inputblock.cls += ' has-feedback';
15041 inputblock.cn.push({
15043 cls: 'glyphicon form-control-feedback'
15050 inputblock.cls += (this.before) ? '' : ' input-group';
15052 inputblock.cn.push({
15054 cls : 'input-group-addon',
15065 cls: 'form-hidden-field'
15079 cls: 'form-hidden-field'
15083 cls: 'roo-select2-choices',
15087 cls: 'roo-select2-search-field',
15100 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15106 if(!this.multiple && this.showToggleBtn){
15113 if (this.caret != false) {
15116 cls: 'fa fa-' + this.caret
15123 cls : 'input-group-addon btn dropdown-toggle',
15128 cls: 'combobox-clear',
15142 combobox.cls += ' roo-select2-container-multi';
15145 var align = this.labelAlign || this.parentLabelAlign();
15147 if (align ==='left' && this.fieldLabel.length) {
15152 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15153 tooltip : 'This field is required'
15157 cls : 'control-label',
15158 html : this.fieldLabel
15169 var labelCfg = cfg.cn[1];
15170 var contentCfg = cfg.cn[2];
15173 if(this.indicatorpos == 'right'){
15178 cls : 'control-label',
15182 html : this.fieldLabel
15186 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15187 tooltip : 'This field is required'
15200 labelCfg = cfg.cn[0];
15201 contentCfg = cfg.cn[1];
15206 if(this.labelWidth > 12){
15207 labelCfg.style = "width: " + this.labelWidth + 'px';
15210 if(this.labelWidth < 13 && this.labelmd == 0){
15211 this.labelmd = this.labelWidth;
15214 if(this.labellg > 0){
15215 labelCfg.cls += ' col-lg-' + this.labellg;
15216 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15219 if(this.labelmd > 0){
15220 labelCfg.cls += ' col-md-' + this.labelmd;
15221 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15224 if(this.labelsm > 0){
15225 labelCfg.cls += ' col-sm-' + this.labelsm;
15226 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15229 if(this.labelxs > 0){
15230 labelCfg.cls += ' col-xs-' + this.labelxs;
15231 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15235 } else if ( this.fieldLabel.length) {
15239 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15240 tooltip : 'This field is required'
15244 cls : 'control-label',
15245 html : this.fieldLabel
15256 if(this.indicatorpos == 'right'){
15260 cls : 'control-label',
15261 html : this.fieldLabel,
15265 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15266 tooltip : 'This field is required'
15283 var settings = this;
15285 ['xs','sm','md','lg'].map(function(size){
15286 if (settings[size]) {
15287 cfg.cls += ' col-' + size + '-' + settings[size];
15294 initTouchView : function()
15296 this.renderTouchView();
15298 this.touchViewEl.on('scroll', function(){
15299 this.el.dom.scrollTop = 0;
15302 this.originalValue = this.getValue();
15304 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15306 this.inputEl().on("click", this.showTouchView, this);
15307 if (this.triggerEl) {
15308 this.triggerEl.on("click", this.showTouchView, this);
15312 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15313 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15315 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15317 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15318 this.store.on('load', this.onTouchViewLoad, this);
15319 this.store.on('loadexception', this.onTouchViewLoadException, this);
15321 if(this.hiddenName){
15323 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15325 this.hiddenField.dom.value =
15326 this.hiddenValue !== undefined ? this.hiddenValue :
15327 this.value !== undefined ? this.value : '';
15329 this.el.dom.removeAttribute('name');
15330 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15334 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15335 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15338 if(this.removable && !this.multiple){
15339 var close = this.closeTriggerEl();
15341 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15342 close.on('click', this.removeBtnClick, this, close);
15346 * fix the bug in Safari iOS8
15348 this.inputEl().on("focus", function(e){
15349 document.activeElement.blur();
15352 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15359 renderTouchView : function()
15361 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15362 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15364 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15365 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15367 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15368 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15369 this.touchViewBodyEl.setStyle('overflow', 'auto');
15371 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15372 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15374 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15375 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15379 showTouchView : function()
15385 this.touchViewHeaderEl.hide();
15387 if(this.modalTitle.length){
15388 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15389 this.touchViewHeaderEl.show();
15392 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15393 this.touchViewEl.show();
15395 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15397 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15398 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15400 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15402 if(this.modalTitle.length){
15403 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15406 this.touchViewBodyEl.setHeight(bodyHeight);
15410 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15412 this.touchViewEl.addClass('in');
15415 if(this._touchViewMask){
15416 Roo.get(document.body).addClass("x-body-masked");
15417 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15418 this._touchViewMask.setStyle('z-index', 10000);
15419 this._touchViewMask.addClass('show');
15422 this.doTouchViewQuery();
15426 hideTouchView : function()
15428 this.touchViewEl.removeClass('in');
15432 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15434 this.touchViewEl.setStyle('display', 'none');
15437 if(this._touchViewMask){
15438 this._touchViewMask.removeClass('show');
15439 Roo.get(document.body).removeClass("x-body-masked");
15443 setTouchViewValue : function()
15450 Roo.each(this.tickItems, function(o){
15455 this.hideTouchView();
15458 doTouchViewQuery : function()
15467 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15471 if(!this.alwaysQuery || this.mode == 'local'){
15472 this.onTouchViewLoad();
15479 onTouchViewBeforeLoad : function(combo,opts)
15485 onTouchViewLoad : function()
15487 if(this.store.getCount() < 1){
15488 this.onTouchViewEmptyResults();
15492 this.clearTouchView();
15494 var rawValue = this.getRawValue();
15496 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15498 this.tickItems = [];
15500 this.store.data.each(function(d, rowIndex){
15501 var row = this.touchViewListGroup.createChild(template);
15503 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15504 row.addClass(d.data.cls);
15507 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15510 html : d.data[this.displayField]
15513 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15514 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15517 row.removeClass('selected');
15518 if(!this.multiple && this.valueField &&
15519 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15522 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15523 row.addClass('selected');
15526 if(this.multiple && this.valueField &&
15527 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15531 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15532 this.tickItems.push(d.data);
15535 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15539 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15541 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15543 if(this.modalTitle.length){
15544 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15547 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15549 if(this.mobile_restrict_height && listHeight < bodyHeight){
15550 this.touchViewBodyEl.setHeight(listHeight);
15555 if(firstChecked && listHeight > bodyHeight){
15556 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15561 onTouchViewLoadException : function()
15563 this.hideTouchView();
15566 onTouchViewEmptyResults : function()
15568 this.clearTouchView();
15570 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15572 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15576 clearTouchView : function()
15578 this.touchViewListGroup.dom.innerHTML = '';
15581 onTouchViewClick : function(e, el, o)
15583 e.preventDefault();
15586 var rowIndex = o.rowIndex;
15588 var r = this.store.getAt(rowIndex);
15590 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15592 if(!this.multiple){
15593 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15594 c.dom.removeAttribute('checked');
15597 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15599 this.setFromData(r.data);
15601 var close = this.closeTriggerEl();
15607 this.hideTouchView();
15609 this.fireEvent('select', this, r, rowIndex);
15614 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15615 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15616 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15620 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15621 this.addItem(r.data);
15622 this.tickItems.push(r.data);
15626 getAutoCreateNativeIOS : function()
15629 cls: 'form-group' //input-group,
15634 cls : 'roo-ios-select'
15638 combobox.name = this.name;
15641 if (this.disabled) {
15642 combobox.disabled = true;
15645 var settings = this;
15647 ['xs','sm','md','lg'].map(function(size){
15648 if (settings[size]) {
15649 cfg.cls += ' col-' + size + '-' + settings[size];
15659 initIOSView : function()
15661 this.store.on('load', this.onIOSViewLoad, this);
15666 onIOSViewLoad : function()
15668 if(this.store.getCount() < 1){
15672 this.clearIOSView();
15674 if(this.allowBlank) {
15676 var default_text = '-- SELECT --';
15678 if(this.placeholder.length){
15679 default_text = this.placeholder;
15682 if(this.emptyTitle.length){
15683 default_text += ' - ' + this.emptyTitle + ' -';
15686 var opt = this.inputEl().createChild({
15689 html : default_text
15693 o[this.valueField] = 0;
15694 o[this.displayField] = default_text;
15696 this.ios_options.push({
15703 this.store.data.each(function(d, rowIndex){
15707 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15708 html = d.data[this.displayField];
15713 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15714 value = d.data[this.valueField];
15723 if(this.value == d.data[this.valueField]){
15724 option['selected'] = true;
15727 var opt = this.inputEl().createChild(option);
15729 this.ios_options.push({
15736 this.inputEl().on('change', function(){
15737 this.fireEvent('select', this);
15742 clearIOSView: function()
15744 this.inputEl().dom.innerHTML = '';
15746 this.ios_options = [];
15749 setIOSValue: function(v)
15753 if(!this.ios_options){
15757 Roo.each(this.ios_options, function(opts){
15759 opts.el.dom.removeAttribute('selected');
15761 if(opts.data[this.valueField] != v){
15765 opts.el.dom.setAttribute('selected', true);
15771 * @cfg {Boolean} grow
15775 * @cfg {Number} growMin
15779 * @cfg {Number} growMax
15788 Roo.apply(Roo.bootstrap.ComboBox, {
15792 cls: 'modal-header',
15814 cls: 'list-group-item',
15818 cls: 'roo-combobox-list-group-item-value'
15822 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15836 listItemCheckbox : {
15838 cls: 'list-group-item',
15842 cls: 'roo-combobox-list-group-item-value'
15846 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15862 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15867 cls: 'modal-footer',
15875 cls: 'col-xs-6 text-left',
15878 cls: 'btn btn-danger roo-touch-view-cancel',
15884 cls: 'col-xs-6 text-right',
15887 cls: 'btn btn-success roo-touch-view-ok',
15898 Roo.apply(Roo.bootstrap.ComboBox, {
15900 touchViewTemplate : {
15902 cls: 'modal fade roo-combobox-touch-view',
15906 cls: 'modal-dialog',
15907 style : 'position:fixed', // we have to fix position....
15911 cls: 'modal-content',
15913 Roo.bootstrap.ComboBox.header,
15914 Roo.bootstrap.ComboBox.body,
15915 Roo.bootstrap.ComboBox.footer
15924 * Ext JS Library 1.1.1
15925 * Copyright(c) 2006-2007, Ext JS, LLC.
15927 * Originally Released Under LGPL - original licence link has changed is not relivant.
15930 * <script type="text/javascript">
15935 * @extends Roo.util.Observable
15936 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15937 * This class also supports single and multi selection modes. <br>
15938 * Create a data model bound view:
15940 var store = new Roo.data.Store(...);
15942 var view = new Roo.View({
15944 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15946 singleSelect: true,
15947 selectedClass: "ydataview-selected",
15951 // listen for node click?
15952 view.on("click", function(vw, index, node, e){
15953 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15957 dataModel.load("foobar.xml");
15959 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15961 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15962 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15964 * Note: old style constructor is still suported (container, template, config)
15967 * Create a new View
15968 * @param {Object} config The config object
15971 Roo.View = function(config, depreciated_tpl, depreciated_config){
15973 this.parent = false;
15975 if (typeof(depreciated_tpl) == 'undefined') {
15976 // new way.. - universal constructor.
15977 Roo.apply(this, config);
15978 this.el = Roo.get(this.el);
15981 this.el = Roo.get(config);
15982 this.tpl = depreciated_tpl;
15983 Roo.apply(this, depreciated_config);
15985 this.wrapEl = this.el.wrap().wrap();
15986 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15989 if(typeof(this.tpl) == "string"){
15990 this.tpl = new Roo.Template(this.tpl);
15992 // support xtype ctors..
15993 this.tpl = new Roo.factory(this.tpl, Roo);
15997 this.tpl.compile();
16002 * @event beforeclick
16003 * Fires before a click is processed. Returns false to cancel the default action.
16004 * @param {Roo.View} this
16005 * @param {Number} index The index of the target node
16006 * @param {HTMLElement} node The target node
16007 * @param {Roo.EventObject} e The raw event object
16009 "beforeclick" : true,
16012 * Fires when a template node is clicked.
16013 * @param {Roo.View} this
16014 * @param {Number} index The index of the target node
16015 * @param {HTMLElement} node The target node
16016 * @param {Roo.EventObject} e The raw event object
16021 * Fires when a template node is double clicked.
16022 * @param {Roo.View} this
16023 * @param {Number} index The index of the target node
16024 * @param {HTMLElement} node The target node
16025 * @param {Roo.EventObject} e The raw event object
16029 * @event contextmenu
16030 * Fires when a template node is right clicked.
16031 * @param {Roo.View} this
16032 * @param {Number} index The index of the target node
16033 * @param {HTMLElement} node The target node
16034 * @param {Roo.EventObject} e The raw event object
16036 "contextmenu" : true,
16038 * @event selectionchange
16039 * Fires when the selected nodes change.
16040 * @param {Roo.View} this
16041 * @param {Array} selections Array of the selected nodes
16043 "selectionchange" : true,
16046 * @event beforeselect
16047 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16048 * @param {Roo.View} this
16049 * @param {HTMLElement} node The node to be selected
16050 * @param {Array} selections Array of currently selected nodes
16052 "beforeselect" : true,
16054 * @event preparedata
16055 * Fires on every row to render, to allow you to change the data.
16056 * @param {Roo.View} this
16057 * @param {Object} data to be rendered (change this)
16059 "preparedata" : true
16067 "click": this.onClick,
16068 "dblclick": this.onDblClick,
16069 "contextmenu": this.onContextMenu,
16073 this.selections = [];
16075 this.cmp = new Roo.CompositeElementLite([]);
16077 this.store = Roo.factory(this.store, Roo.data);
16078 this.setStore(this.store, true);
16081 if ( this.footer && this.footer.xtype) {
16083 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16085 this.footer.dataSource = this.store;
16086 this.footer.container = fctr;
16087 this.footer = Roo.factory(this.footer, Roo);
16088 fctr.insertFirst(this.el);
16090 // this is a bit insane - as the paging toolbar seems to detach the el..
16091 // dom.parentNode.parentNode.parentNode
16092 // they get detached?
16096 Roo.View.superclass.constructor.call(this);
16101 Roo.extend(Roo.View, Roo.util.Observable, {
16104 * @cfg {Roo.data.Store} store Data store to load data from.
16109 * @cfg {String|Roo.Element} el The container element.
16114 * @cfg {String|Roo.Template} tpl The template used by this View
16118 * @cfg {String} dataName the named area of the template to use as the data area
16119 * Works with domtemplates roo-name="name"
16123 * @cfg {String} selectedClass The css class to add to selected nodes
16125 selectedClass : "x-view-selected",
16127 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16132 * @cfg {String} text to display on mask (default Loading)
16136 * @cfg {Boolean} multiSelect Allow multiple selection
16138 multiSelect : false,
16140 * @cfg {Boolean} singleSelect Allow single selection
16142 singleSelect: false,
16145 * @cfg {Boolean} toggleSelect - selecting
16147 toggleSelect : false,
16150 * @cfg {Boolean} tickable - selecting
16155 * Returns the element this view is bound to.
16156 * @return {Roo.Element}
16158 getEl : function(){
16159 return this.wrapEl;
16165 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16167 refresh : function(){
16168 //Roo.log('refresh');
16171 // if we are using something like 'domtemplate', then
16172 // the what gets used is:
16173 // t.applySubtemplate(NAME, data, wrapping data..)
16174 // the outer template then get' applied with
16175 // the store 'extra data'
16176 // and the body get's added to the
16177 // roo-name="data" node?
16178 // <span class='roo-tpl-{name}'></span> ?????
16182 this.clearSelections();
16183 this.el.update("");
16185 var records = this.store.getRange();
16186 if(records.length < 1) {
16188 // is this valid?? = should it render a template??
16190 this.el.update(this.emptyText);
16194 if (this.dataName) {
16195 this.el.update(t.apply(this.store.meta)); //????
16196 el = this.el.child('.roo-tpl-' + this.dataName);
16199 for(var i = 0, len = records.length; i < len; i++){
16200 var data = this.prepareData(records[i].data, i, records[i]);
16201 this.fireEvent("preparedata", this, data, i, records[i]);
16203 var d = Roo.apply({}, data);
16206 Roo.apply(d, {'roo-id' : Roo.id()});
16210 Roo.each(this.parent.item, function(item){
16211 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16214 Roo.apply(d, {'roo-data-checked' : 'checked'});
16218 html[html.length] = Roo.util.Format.trim(
16220 t.applySubtemplate(this.dataName, d, this.store.meta) :
16227 el.update(html.join(""));
16228 this.nodes = el.dom.childNodes;
16229 this.updateIndexes(0);
16234 * Function to override to reformat the data that is sent to
16235 * the template for each node.
16236 * DEPRICATED - use the preparedata event handler.
16237 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16238 * a JSON object for an UpdateManager bound view).
16240 prepareData : function(data, index, record)
16242 this.fireEvent("preparedata", this, data, index, record);
16246 onUpdate : function(ds, record){
16247 // Roo.log('on update');
16248 this.clearSelections();
16249 var index = this.store.indexOf(record);
16250 var n = this.nodes[index];
16251 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16252 n.parentNode.removeChild(n);
16253 this.updateIndexes(index, index);
16259 onAdd : function(ds, records, index)
16261 //Roo.log(['on Add', ds, records, index] );
16262 this.clearSelections();
16263 if(this.nodes.length == 0){
16267 var n = this.nodes[index];
16268 for(var i = 0, len = records.length; i < len; i++){
16269 var d = this.prepareData(records[i].data, i, records[i]);
16271 this.tpl.insertBefore(n, d);
16274 this.tpl.append(this.el, d);
16277 this.updateIndexes(index);
16280 onRemove : function(ds, record, index){
16281 // Roo.log('onRemove');
16282 this.clearSelections();
16283 var el = this.dataName ?
16284 this.el.child('.roo-tpl-' + this.dataName) :
16287 el.dom.removeChild(this.nodes[index]);
16288 this.updateIndexes(index);
16292 * Refresh an individual node.
16293 * @param {Number} index
16295 refreshNode : function(index){
16296 this.onUpdate(this.store, this.store.getAt(index));
16299 updateIndexes : function(startIndex, endIndex){
16300 var ns = this.nodes;
16301 startIndex = startIndex || 0;
16302 endIndex = endIndex || ns.length - 1;
16303 for(var i = startIndex; i <= endIndex; i++){
16304 ns[i].nodeIndex = i;
16309 * Changes the data store this view uses and refresh the view.
16310 * @param {Store} store
16312 setStore : function(store, initial){
16313 if(!initial && this.store){
16314 this.store.un("datachanged", this.refresh);
16315 this.store.un("add", this.onAdd);
16316 this.store.un("remove", this.onRemove);
16317 this.store.un("update", this.onUpdate);
16318 this.store.un("clear", this.refresh);
16319 this.store.un("beforeload", this.onBeforeLoad);
16320 this.store.un("load", this.onLoad);
16321 this.store.un("loadexception", this.onLoad);
16325 store.on("datachanged", this.refresh, this);
16326 store.on("add", this.onAdd, this);
16327 store.on("remove", this.onRemove, this);
16328 store.on("update", this.onUpdate, this);
16329 store.on("clear", this.refresh, this);
16330 store.on("beforeload", this.onBeforeLoad, this);
16331 store.on("load", this.onLoad, this);
16332 store.on("loadexception", this.onLoad, this);
16340 * onbeforeLoad - masks the loading area.
16343 onBeforeLoad : function(store,opts)
16345 //Roo.log('onBeforeLoad');
16347 this.el.update("");
16349 this.el.mask(this.mask ? this.mask : "Loading" );
16351 onLoad : function ()
16358 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16359 * @param {HTMLElement} node
16360 * @return {HTMLElement} The template node
16362 findItemFromChild : function(node){
16363 var el = this.dataName ?
16364 this.el.child('.roo-tpl-' + this.dataName,true) :
16367 if(!node || node.parentNode == el){
16370 var p = node.parentNode;
16371 while(p && p != el){
16372 if(p.parentNode == el){
16381 onClick : function(e){
16382 var item = this.findItemFromChild(e.getTarget());
16384 var index = this.indexOf(item);
16385 if(this.onItemClick(item, index, e) !== false){
16386 this.fireEvent("click", this, index, item, e);
16389 this.clearSelections();
16394 onContextMenu : function(e){
16395 var item = this.findItemFromChild(e.getTarget());
16397 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16402 onDblClick : function(e){
16403 var item = this.findItemFromChild(e.getTarget());
16405 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16409 onItemClick : function(item, index, e)
16411 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16414 if (this.toggleSelect) {
16415 var m = this.isSelected(item) ? 'unselect' : 'select';
16418 _t[m](item, true, false);
16421 if(this.multiSelect || this.singleSelect){
16422 if(this.multiSelect && e.shiftKey && this.lastSelection){
16423 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16425 this.select(item, this.multiSelect && e.ctrlKey);
16426 this.lastSelection = item;
16429 if(!this.tickable){
16430 e.preventDefault();
16438 * Get the number of selected nodes.
16441 getSelectionCount : function(){
16442 return this.selections.length;
16446 * Get the currently selected nodes.
16447 * @return {Array} An array of HTMLElements
16449 getSelectedNodes : function(){
16450 return this.selections;
16454 * Get the indexes of the selected nodes.
16457 getSelectedIndexes : function(){
16458 var indexes = [], s = this.selections;
16459 for(var i = 0, len = s.length; i < len; i++){
16460 indexes.push(s[i].nodeIndex);
16466 * Clear all selections
16467 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16469 clearSelections : function(suppressEvent){
16470 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16471 this.cmp.elements = this.selections;
16472 this.cmp.removeClass(this.selectedClass);
16473 this.selections = [];
16474 if(!suppressEvent){
16475 this.fireEvent("selectionchange", this, this.selections);
16481 * Returns true if the passed node is selected
16482 * @param {HTMLElement/Number} node The node or node index
16483 * @return {Boolean}
16485 isSelected : function(node){
16486 var s = this.selections;
16490 node = this.getNode(node);
16491 return s.indexOf(node) !== -1;
16496 * @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
16497 * @param {Boolean} keepExisting (optional) true to keep existing selections
16498 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16500 select : function(nodeInfo, keepExisting, suppressEvent){
16501 if(nodeInfo instanceof Array){
16503 this.clearSelections(true);
16505 for(var i = 0, len = nodeInfo.length; i < len; i++){
16506 this.select(nodeInfo[i], true, true);
16510 var node = this.getNode(nodeInfo);
16511 if(!node || this.isSelected(node)){
16512 return; // already selected.
16515 this.clearSelections(true);
16518 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16519 Roo.fly(node).addClass(this.selectedClass);
16520 this.selections.push(node);
16521 if(!suppressEvent){
16522 this.fireEvent("selectionchange", this, this.selections);
16530 * @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
16531 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16532 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16534 unselect : function(nodeInfo, keepExisting, suppressEvent)
16536 if(nodeInfo instanceof Array){
16537 Roo.each(this.selections, function(s) {
16538 this.unselect(s, nodeInfo);
16542 var node = this.getNode(nodeInfo);
16543 if(!node || !this.isSelected(node)){
16544 //Roo.log("not selected");
16545 return; // not selected.
16549 Roo.each(this.selections, function(s) {
16551 Roo.fly(node).removeClass(this.selectedClass);
16558 this.selections= ns;
16559 this.fireEvent("selectionchange", this, this.selections);
16563 * Gets a template node.
16564 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16565 * @return {HTMLElement} The node or null if it wasn't found
16567 getNode : function(nodeInfo){
16568 if(typeof nodeInfo == "string"){
16569 return document.getElementById(nodeInfo);
16570 }else if(typeof nodeInfo == "number"){
16571 return this.nodes[nodeInfo];
16577 * Gets a range template nodes.
16578 * @param {Number} startIndex
16579 * @param {Number} endIndex
16580 * @return {Array} An array of nodes
16582 getNodes : function(start, end){
16583 var ns = this.nodes;
16584 start = start || 0;
16585 end = typeof end == "undefined" ? ns.length - 1 : end;
16588 for(var i = start; i <= end; i++){
16592 for(var i = start; i >= end; i--){
16600 * Finds the index of the passed node
16601 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16602 * @return {Number} The index of the node or -1
16604 indexOf : function(node){
16605 node = this.getNode(node);
16606 if(typeof node.nodeIndex == "number"){
16607 return node.nodeIndex;
16609 var ns = this.nodes;
16610 for(var i = 0, len = ns.length; i < len; i++){
16621 * based on jquery fullcalendar
16625 Roo.bootstrap = Roo.bootstrap || {};
16627 * @class Roo.bootstrap.Calendar
16628 * @extends Roo.bootstrap.Component
16629 * Bootstrap Calendar class
16630 * @cfg {Boolean} loadMask (true|false) default false
16631 * @cfg {Object} header generate the user specific header of the calendar, default false
16634 * Create a new Container
16635 * @param {Object} config The config object
16640 Roo.bootstrap.Calendar = function(config){
16641 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16645 * Fires when a date is selected
16646 * @param {DatePicker} this
16647 * @param {Date} date The selected date
16651 * @event monthchange
16652 * Fires when the displayed month changes
16653 * @param {DatePicker} this
16654 * @param {Date} date The selected month
16656 'monthchange': true,
16658 * @event evententer
16659 * Fires when mouse over an event
16660 * @param {Calendar} this
16661 * @param {event} Event
16663 'evententer': true,
16665 * @event eventleave
16666 * Fires when the mouse leaves an
16667 * @param {Calendar} this
16670 'eventleave': true,
16672 * @event eventclick
16673 * Fires when the mouse click an
16674 * @param {Calendar} this
16683 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16686 * @cfg {Number} startDay
16687 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16695 getAutoCreate : function(){
16698 var fc_button = function(name, corner, style, content ) {
16699 return Roo.apply({},{
16701 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16703 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16706 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16717 style : 'width:100%',
16724 cls : 'fc-header-left',
16726 fc_button('prev', 'left', 'arrow', '‹' ),
16727 fc_button('next', 'right', 'arrow', '›' ),
16728 { tag: 'span', cls: 'fc-header-space' },
16729 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16737 cls : 'fc-header-center',
16741 cls: 'fc-header-title',
16744 html : 'month / year'
16752 cls : 'fc-header-right',
16754 /* fc_button('month', 'left', '', 'month' ),
16755 fc_button('week', '', '', 'week' ),
16756 fc_button('day', 'right', '', 'day' )
16768 header = this.header;
16771 var cal_heads = function() {
16773 // fixme - handle this.
16775 for (var i =0; i < Date.dayNames.length; i++) {
16776 var d = Date.dayNames[i];
16779 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16780 html : d.substring(0,3)
16784 ret[0].cls += ' fc-first';
16785 ret[6].cls += ' fc-last';
16788 var cal_cell = function(n) {
16791 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16796 cls: 'fc-day-number',
16800 cls: 'fc-day-content',
16804 style: 'position: relative;' // height: 17px;
16816 var cal_rows = function() {
16819 for (var r = 0; r < 6; r++) {
16826 for (var i =0; i < Date.dayNames.length; i++) {
16827 var d = Date.dayNames[i];
16828 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16831 row.cn[0].cls+=' fc-first';
16832 row.cn[0].cn[0].style = 'min-height:90px';
16833 row.cn[6].cls+=' fc-last';
16837 ret[0].cls += ' fc-first';
16838 ret[4].cls += ' fc-prev-last';
16839 ret[5].cls += ' fc-last';
16846 cls: 'fc-border-separate',
16847 style : 'width:100%',
16855 cls : 'fc-first fc-last',
16873 cls : 'fc-content',
16874 style : "position: relative;",
16877 cls : 'fc-view fc-view-month fc-grid',
16878 style : 'position: relative',
16879 unselectable : 'on',
16882 cls : 'fc-event-container',
16883 style : 'position:absolute;z-index:8;top:0;left:0;'
16901 initEvents : function()
16904 throw "can not find store for calendar";
16910 style: "text-align:center",
16914 style: "background-color:white;width:50%;margin:250 auto",
16918 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16929 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16931 var size = this.el.select('.fc-content', true).first().getSize();
16932 this.maskEl.setSize(size.width, size.height);
16933 this.maskEl.enableDisplayMode("block");
16934 if(!this.loadMask){
16935 this.maskEl.hide();
16938 this.store = Roo.factory(this.store, Roo.data);
16939 this.store.on('load', this.onLoad, this);
16940 this.store.on('beforeload', this.onBeforeLoad, this);
16944 this.cells = this.el.select('.fc-day',true);
16945 //Roo.log(this.cells);
16946 this.textNodes = this.el.query('.fc-day-number');
16947 this.cells.addClassOnOver('fc-state-hover');
16949 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16950 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16951 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16952 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16954 this.on('monthchange', this.onMonthChange, this);
16956 this.update(new Date().clearTime());
16959 resize : function() {
16960 var sz = this.el.getSize();
16962 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16963 this.el.select('.fc-day-content div',true).setHeight(34);
16968 showPrevMonth : function(e){
16969 this.update(this.activeDate.add("mo", -1));
16971 showToday : function(e){
16972 this.update(new Date().clearTime());
16975 showNextMonth : function(e){
16976 this.update(this.activeDate.add("mo", 1));
16980 showPrevYear : function(){
16981 this.update(this.activeDate.add("y", -1));
16985 showNextYear : function(){
16986 this.update(this.activeDate.add("y", 1));
16991 update : function(date)
16993 var vd = this.activeDate;
16994 this.activeDate = date;
16995 // if(vd && this.el){
16996 // var t = date.getTime();
16997 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16998 // Roo.log('using add remove');
17000 // this.fireEvent('monthchange', this, date);
17002 // this.cells.removeClass("fc-state-highlight");
17003 // this.cells.each(function(c){
17004 // if(c.dateValue == t){
17005 // c.addClass("fc-state-highlight");
17006 // setTimeout(function(){
17007 // try{c.dom.firstChild.focus();}catch(e){}
17017 var days = date.getDaysInMonth();
17019 var firstOfMonth = date.getFirstDateOfMonth();
17020 var startingPos = firstOfMonth.getDay()-this.startDay;
17022 if(startingPos < this.startDay){
17026 var pm = date.add(Date.MONTH, -1);
17027 var prevStart = pm.getDaysInMonth()-startingPos;
17029 this.cells = this.el.select('.fc-day',true);
17030 this.textNodes = this.el.query('.fc-day-number');
17031 this.cells.addClassOnOver('fc-state-hover');
17033 var cells = this.cells.elements;
17034 var textEls = this.textNodes;
17036 Roo.each(cells, function(cell){
17037 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17040 days += startingPos;
17042 // convert everything to numbers so it's fast
17043 var day = 86400000;
17044 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17047 //Roo.log(prevStart);
17049 var today = new Date().clearTime().getTime();
17050 var sel = date.clearTime().getTime();
17051 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17052 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17053 var ddMatch = this.disabledDatesRE;
17054 var ddText = this.disabledDatesText;
17055 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17056 var ddaysText = this.disabledDaysText;
17057 var format = this.format;
17059 var setCellClass = function(cal, cell){
17063 //Roo.log('set Cell Class');
17065 var t = d.getTime();
17069 cell.dateValue = t;
17071 cell.className += " fc-today";
17072 cell.className += " fc-state-highlight";
17073 cell.title = cal.todayText;
17076 // disable highlight in other month..
17077 //cell.className += " fc-state-highlight";
17082 cell.className = " fc-state-disabled";
17083 cell.title = cal.minText;
17087 cell.className = " fc-state-disabled";
17088 cell.title = cal.maxText;
17092 if(ddays.indexOf(d.getDay()) != -1){
17093 cell.title = ddaysText;
17094 cell.className = " fc-state-disabled";
17097 if(ddMatch && format){
17098 var fvalue = d.dateFormat(format);
17099 if(ddMatch.test(fvalue)){
17100 cell.title = ddText.replace("%0", fvalue);
17101 cell.className = " fc-state-disabled";
17105 if (!cell.initialClassName) {
17106 cell.initialClassName = cell.dom.className;
17109 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17114 for(; i < startingPos; i++) {
17115 textEls[i].innerHTML = (++prevStart);
17116 d.setDate(d.getDate()+1);
17118 cells[i].className = "fc-past fc-other-month";
17119 setCellClass(this, cells[i]);
17124 for(; i < days; i++){
17125 intDay = i - startingPos + 1;
17126 textEls[i].innerHTML = (intDay);
17127 d.setDate(d.getDate()+1);
17129 cells[i].className = ''; // "x-date-active";
17130 setCellClass(this, cells[i]);
17134 for(; i < 42; i++) {
17135 textEls[i].innerHTML = (++extraDays);
17136 d.setDate(d.getDate()+1);
17138 cells[i].className = "fc-future fc-other-month";
17139 setCellClass(this, cells[i]);
17142 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17144 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17146 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17147 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17149 if(totalRows != 6){
17150 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17151 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17154 this.fireEvent('monthchange', this, date);
17158 if(!this.internalRender){
17159 var main = this.el.dom.firstChild;
17160 var w = main.offsetWidth;
17161 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17162 Roo.fly(main).setWidth(w);
17163 this.internalRender = true;
17164 // opera does not respect the auto grow header center column
17165 // then, after it gets a width opera refuses to recalculate
17166 // without a second pass
17167 if(Roo.isOpera && !this.secondPass){
17168 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17169 this.secondPass = true;
17170 this.update.defer(10, this, [date]);
17177 findCell : function(dt) {
17178 dt = dt.clearTime().getTime();
17180 this.cells.each(function(c){
17181 //Roo.log("check " +c.dateValue + '?=' + dt);
17182 if(c.dateValue == dt){
17192 findCells : function(ev) {
17193 var s = ev.start.clone().clearTime().getTime();
17195 var e= ev.end.clone().clearTime().getTime();
17198 this.cells.each(function(c){
17199 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17201 if(c.dateValue > e){
17204 if(c.dateValue < s){
17213 // findBestRow: function(cells)
17217 // for (var i =0 ; i < cells.length;i++) {
17218 // ret = Math.max(cells[i].rows || 0,ret);
17225 addItem : function(ev)
17227 // look for vertical location slot in
17228 var cells = this.findCells(ev);
17230 // ev.row = this.findBestRow(cells);
17232 // work out the location.
17236 for(var i =0; i < cells.length; i++) {
17238 cells[i].row = cells[0].row;
17241 cells[i].row = cells[i].row + 1;
17251 if (crow.start.getY() == cells[i].getY()) {
17253 crow.end = cells[i];
17270 cells[0].events.push(ev);
17272 this.calevents.push(ev);
17275 clearEvents: function() {
17277 if(!this.calevents){
17281 Roo.each(this.cells.elements, function(c){
17287 Roo.each(this.calevents, function(e) {
17288 Roo.each(e.els, function(el) {
17289 el.un('mouseenter' ,this.onEventEnter, this);
17290 el.un('mouseleave' ,this.onEventLeave, this);
17295 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17301 renderEvents: function()
17305 this.cells.each(function(c) {
17314 if(c.row != c.events.length){
17315 r = 4 - (4 - (c.row - c.events.length));
17318 c.events = ev.slice(0, r);
17319 c.more = ev.slice(r);
17321 if(c.more.length && c.more.length == 1){
17322 c.events.push(c.more.pop());
17325 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17329 this.cells.each(function(c) {
17331 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17334 for (var e = 0; e < c.events.length; e++){
17335 var ev = c.events[e];
17336 var rows = ev.rows;
17338 for(var i = 0; i < rows.length; i++) {
17340 // how many rows should it span..
17343 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17344 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17346 unselectable : "on",
17349 cls: 'fc-event-inner',
17353 // cls: 'fc-event-time',
17354 // html : cells.length > 1 ? '' : ev.time
17358 cls: 'fc-event-title',
17359 html : String.format('{0}', ev.title)
17366 cls: 'ui-resizable-handle ui-resizable-e',
17367 html : '  '
17374 cfg.cls += ' fc-event-start';
17376 if ((i+1) == rows.length) {
17377 cfg.cls += ' fc-event-end';
17380 var ctr = _this.el.select('.fc-event-container',true).first();
17381 var cg = ctr.createChild(cfg);
17383 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17384 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17386 var r = (c.more.length) ? 1 : 0;
17387 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17388 cg.setWidth(ebox.right - sbox.x -2);
17390 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17391 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17392 cg.on('click', _this.onEventClick, _this, ev);
17403 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17404 style : 'position: absolute',
17405 unselectable : "on",
17408 cls: 'fc-event-inner',
17412 cls: 'fc-event-title',
17420 cls: 'ui-resizable-handle ui-resizable-e',
17421 html : '  '
17427 var ctr = _this.el.select('.fc-event-container',true).first();
17428 var cg = ctr.createChild(cfg);
17430 var sbox = c.select('.fc-day-content',true).first().getBox();
17431 var ebox = c.select('.fc-day-content',true).first().getBox();
17433 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17434 cg.setWidth(ebox.right - sbox.x -2);
17436 cg.on('click', _this.onMoreEventClick, _this, c.more);
17446 onEventEnter: function (e, el,event,d) {
17447 this.fireEvent('evententer', this, el, event);
17450 onEventLeave: function (e, el,event,d) {
17451 this.fireEvent('eventleave', this, el, event);
17454 onEventClick: function (e, el,event,d) {
17455 this.fireEvent('eventclick', this, el, event);
17458 onMonthChange: function () {
17462 onMoreEventClick: function(e, el, more)
17466 this.calpopover.placement = 'right';
17467 this.calpopover.setTitle('More');
17469 this.calpopover.setContent('');
17471 var ctr = this.calpopover.el.select('.popover-content', true).first();
17473 Roo.each(more, function(m){
17475 cls : 'fc-event-hori fc-event-draggable',
17478 var cg = ctr.createChild(cfg);
17480 cg.on('click', _this.onEventClick, _this, m);
17483 this.calpopover.show(el);
17488 onLoad: function ()
17490 this.calevents = [];
17493 if(this.store.getCount() > 0){
17494 this.store.data.each(function(d){
17497 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17498 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17499 time : d.data.start_time,
17500 title : d.data.title,
17501 description : d.data.description,
17502 venue : d.data.venue
17507 this.renderEvents();
17509 if(this.calevents.length && this.loadMask){
17510 this.maskEl.hide();
17514 onBeforeLoad: function()
17516 this.clearEvents();
17518 this.maskEl.show();
17532 * @class Roo.bootstrap.Popover
17533 * @extends Roo.bootstrap.Component
17534 * Bootstrap Popover class
17535 * @cfg {String} html contents of the popover (or false to use children..)
17536 * @cfg {String} title of popover (or false to hide)
17537 * @cfg {String} placement how it is placed
17538 * @cfg {String} trigger click || hover (or false to trigger manually)
17539 * @cfg {String} over what (parent or false to trigger manually.)
17540 * @cfg {Number} delay - delay before showing
17543 * Create a new Popover
17544 * @param {Object} config The config object
17547 Roo.bootstrap.Popover = function(config){
17548 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17554 * After the popover show
17556 * @param {Roo.bootstrap.Popover} this
17561 * After the popover hide
17563 * @param {Roo.bootstrap.Popover} this
17569 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17571 title: 'Fill in a title',
17574 placement : 'right',
17575 trigger : 'hover', // hover
17581 can_build_overlaid : false,
17583 getChildContainer : function()
17585 return this.el.select('.popover-content',true).first();
17588 getAutoCreate : function(){
17591 cls : 'popover roo-dynamic',
17592 style: 'display:block',
17598 cls : 'popover-inner',
17602 cls: 'popover-title',
17606 cls : 'popover-content',
17617 setTitle: function(str)
17620 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17622 setContent: function(str)
17625 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17627 // as it get's added to the bottom of the page.
17628 onRender : function(ct, position)
17630 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17632 var cfg = Roo.apply({}, this.getAutoCreate());
17636 cfg.cls += ' ' + this.cls;
17639 cfg.style = this.style;
17641 //Roo.log("adding to ");
17642 this.el = Roo.get(document.body).createChild(cfg, position);
17643 // Roo.log(this.el);
17648 initEvents : function()
17650 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17651 this.el.enableDisplayMode('block');
17653 if (this.over === false) {
17656 if (this.triggers === false) {
17659 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17660 var triggers = this.trigger ? this.trigger.split(' ') : [];
17661 Roo.each(triggers, function(trigger) {
17663 if (trigger == 'click') {
17664 on_el.on('click', this.toggle, this);
17665 } else if (trigger != 'manual') {
17666 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17667 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17669 on_el.on(eventIn ,this.enter, this);
17670 on_el.on(eventOut, this.leave, this);
17681 toggle : function () {
17682 this.hoverState == 'in' ? this.leave() : this.enter();
17685 enter : function () {
17687 clearTimeout(this.timeout);
17689 this.hoverState = 'in';
17691 if (!this.delay || !this.delay.show) {
17696 this.timeout = setTimeout(function () {
17697 if (_t.hoverState == 'in') {
17700 }, this.delay.show)
17703 leave : function() {
17704 clearTimeout(this.timeout);
17706 this.hoverState = 'out';
17708 if (!this.delay || !this.delay.hide) {
17713 this.timeout = setTimeout(function () {
17714 if (_t.hoverState == 'out') {
17717 }, this.delay.hide)
17720 show : function (on_el)
17723 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17727 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17728 if (this.html !== false) {
17729 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17731 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17732 if (!this.title.length) {
17733 this.el.select('.popover-title',true).hide();
17736 var placement = typeof this.placement == 'function' ?
17737 this.placement.call(this, this.el, on_el) :
17740 var autoToken = /\s?auto?\s?/i;
17741 var autoPlace = autoToken.test(placement);
17743 placement = placement.replace(autoToken, '') || 'top';
17747 //this.el.setXY([0,0]);
17749 this.el.dom.style.display='block';
17750 this.el.addClass(placement);
17752 //this.el.appendTo(on_el);
17754 var p = this.getPosition();
17755 var box = this.el.getBox();
17760 var align = Roo.bootstrap.Popover.alignment[placement];
17763 this.el.alignTo(on_el, align[0],align[1]);
17764 //var arrow = this.el.select('.arrow',true).first();
17765 //arrow.set(align[2],
17767 this.el.addClass('in');
17770 if (this.el.hasClass('fade')) {
17774 this.hoverState = 'in';
17776 this.fireEvent('show', this);
17781 this.el.setXY([0,0]);
17782 this.el.removeClass('in');
17784 this.hoverState = null;
17786 this.fireEvent('hide', this);
17791 Roo.bootstrap.Popover.alignment = {
17792 'left' : ['r-l', [-10,0], 'right'],
17793 'right' : ['l-r', [10,0], 'left'],
17794 'bottom' : ['t-b', [0,10], 'top'],
17795 'top' : [ 'b-t', [0,-10], 'bottom']
17806 * @class Roo.bootstrap.Progress
17807 * @extends Roo.bootstrap.Component
17808 * Bootstrap Progress class
17809 * @cfg {Boolean} striped striped of the progress bar
17810 * @cfg {Boolean} active animated of the progress bar
17814 * Create a new Progress
17815 * @param {Object} config The config object
17818 Roo.bootstrap.Progress = function(config){
17819 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17822 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17827 getAutoCreate : function(){
17835 cfg.cls += ' progress-striped';
17839 cfg.cls += ' active';
17858 * @class Roo.bootstrap.ProgressBar
17859 * @extends Roo.bootstrap.Component
17860 * Bootstrap ProgressBar class
17861 * @cfg {Number} aria_valuenow aria-value now
17862 * @cfg {Number} aria_valuemin aria-value min
17863 * @cfg {Number} aria_valuemax aria-value max
17864 * @cfg {String} label label for the progress bar
17865 * @cfg {String} panel (success | info | warning | danger )
17866 * @cfg {String} role role of the progress bar
17867 * @cfg {String} sr_only text
17871 * Create a new ProgressBar
17872 * @param {Object} config The config object
17875 Roo.bootstrap.ProgressBar = function(config){
17876 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17879 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17883 aria_valuemax : 100,
17889 getAutoCreate : function()
17894 cls: 'progress-bar',
17895 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17907 cfg.role = this.role;
17910 if(this.aria_valuenow){
17911 cfg['aria-valuenow'] = this.aria_valuenow;
17914 if(this.aria_valuemin){
17915 cfg['aria-valuemin'] = this.aria_valuemin;
17918 if(this.aria_valuemax){
17919 cfg['aria-valuemax'] = this.aria_valuemax;
17922 if(this.label && !this.sr_only){
17923 cfg.html = this.label;
17927 cfg.cls += ' progress-bar-' + this.panel;
17933 update : function(aria_valuenow)
17935 this.aria_valuenow = aria_valuenow;
17937 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17952 * @class Roo.bootstrap.TabGroup
17953 * @extends Roo.bootstrap.Column
17954 * Bootstrap Column class
17955 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17956 * @cfg {Boolean} carousel true to make the group behave like a carousel
17957 * @cfg {Boolean} bullets show bullets for the panels
17958 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17959 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17960 * @cfg {Boolean} showarrow (true|false) show arrow default true
17963 * Create a new TabGroup
17964 * @param {Object} config The config object
17967 Roo.bootstrap.TabGroup = function(config){
17968 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17970 this.navId = Roo.id();
17973 Roo.bootstrap.TabGroup.register(this);
17977 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17980 transition : false,
17985 slideOnTouch : false,
17988 getAutoCreate : function()
17990 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17992 cfg.cls += ' tab-content';
17994 if (this.carousel) {
17995 cfg.cls += ' carousel slide';
17998 cls : 'carousel-inner',
18002 if(this.bullets && !Roo.isTouch){
18005 cls : 'carousel-bullets',
18009 if(this.bullets_cls){
18010 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18017 cfg.cn[0].cn.push(bullets);
18020 if(this.showarrow){
18021 cfg.cn[0].cn.push({
18023 class : 'carousel-arrow',
18027 class : 'carousel-prev',
18031 class : 'fa fa-chevron-left'
18037 class : 'carousel-next',
18041 class : 'fa fa-chevron-right'
18054 initEvents: function()
18056 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18057 // this.el.on("touchstart", this.onTouchStart, this);
18060 if(this.autoslide){
18063 this.slideFn = window.setInterval(function() {
18064 _this.showPanelNext();
18068 if(this.showarrow){
18069 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18070 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18076 // onTouchStart : function(e, el, o)
18078 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18082 // this.showPanelNext();
18086 getChildContainer : function()
18088 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18092 * register a Navigation item
18093 * @param {Roo.bootstrap.NavItem} the navitem to add
18095 register : function(item)
18097 this.tabs.push( item);
18098 item.navId = this.navId; // not really needed..
18103 getActivePanel : function()
18106 Roo.each(this.tabs, function(t) {
18116 getPanelByName : function(n)
18119 Roo.each(this.tabs, function(t) {
18120 if (t.tabId == n) {
18128 indexOfPanel : function(p)
18131 Roo.each(this.tabs, function(t,i) {
18132 if (t.tabId == p.tabId) {
18141 * show a specific panel
18142 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18143 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18145 showPanel : function (pan)
18147 if(this.transition || typeof(pan) == 'undefined'){
18148 Roo.log("waiting for the transitionend");
18152 if (typeof(pan) == 'number') {
18153 pan = this.tabs[pan];
18156 if (typeof(pan) == 'string') {
18157 pan = this.getPanelByName(pan);
18160 var cur = this.getActivePanel();
18163 Roo.log('pan or acitve pan is undefined');
18167 if (pan.tabId == this.getActivePanel().tabId) {
18171 if (false === cur.fireEvent('beforedeactivate')) {
18175 if(this.bullets > 0 && !Roo.isTouch){
18176 this.setActiveBullet(this.indexOfPanel(pan));
18179 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18181 this.transition = true;
18182 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18183 var lr = dir == 'next' ? 'left' : 'right';
18184 pan.el.addClass(dir); // or prev
18185 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18186 cur.el.addClass(lr); // or right
18187 pan.el.addClass(lr);
18190 cur.el.on('transitionend', function() {
18191 Roo.log("trans end?");
18193 pan.el.removeClass([lr,dir]);
18194 pan.setActive(true);
18196 cur.el.removeClass([lr]);
18197 cur.setActive(false);
18199 _this.transition = false;
18201 }, this, { single: true } );
18206 cur.setActive(false);
18207 pan.setActive(true);
18212 showPanelNext : function()
18214 var i = this.indexOfPanel(this.getActivePanel());
18216 if (i >= this.tabs.length - 1 && !this.autoslide) {
18220 if (i >= this.tabs.length - 1 && this.autoslide) {
18224 this.showPanel(this.tabs[i+1]);
18227 showPanelPrev : function()
18229 var i = this.indexOfPanel(this.getActivePanel());
18231 if (i < 1 && !this.autoslide) {
18235 if (i < 1 && this.autoslide) {
18236 i = this.tabs.length;
18239 this.showPanel(this.tabs[i-1]);
18243 addBullet: function()
18245 if(!this.bullets || Roo.isTouch){
18248 var ctr = this.el.select('.carousel-bullets',true).first();
18249 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18250 var bullet = ctr.createChild({
18251 cls : 'bullet bullet-' + i
18252 },ctr.dom.lastChild);
18257 bullet.on('click', (function(e, el, o, ii, t){
18259 e.preventDefault();
18261 this.showPanel(ii);
18263 if(this.autoslide && this.slideFn){
18264 clearInterval(this.slideFn);
18265 this.slideFn = window.setInterval(function() {
18266 _this.showPanelNext();
18270 }).createDelegate(this, [i, bullet], true));
18275 setActiveBullet : function(i)
18281 Roo.each(this.el.select('.bullet', true).elements, function(el){
18282 el.removeClass('selected');
18285 var bullet = this.el.select('.bullet-' + i, true).first();
18291 bullet.addClass('selected');
18302 Roo.apply(Roo.bootstrap.TabGroup, {
18306 * register a Navigation Group
18307 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18309 register : function(navgrp)
18311 this.groups[navgrp.navId] = navgrp;
18315 * fetch a Navigation Group based on the navigation ID
18316 * if one does not exist , it will get created.
18317 * @param {string} the navgroup to add
18318 * @returns {Roo.bootstrap.NavGroup} the navgroup
18320 get: function(navId) {
18321 if (typeof(this.groups[navId]) == 'undefined') {
18322 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18324 return this.groups[navId] ;
18339 * @class Roo.bootstrap.TabPanel
18340 * @extends Roo.bootstrap.Component
18341 * Bootstrap TabPanel class
18342 * @cfg {Boolean} active panel active
18343 * @cfg {String} html panel content
18344 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18345 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18346 * @cfg {String} href click to link..
18350 * Create a new TabPanel
18351 * @param {Object} config The config object
18354 Roo.bootstrap.TabPanel = function(config){
18355 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18359 * Fires when the active status changes
18360 * @param {Roo.bootstrap.TabPanel} this
18361 * @param {Boolean} state the new state
18366 * @event beforedeactivate
18367 * Fires before a tab is de-activated - can be used to do validation on a form.
18368 * @param {Roo.bootstrap.TabPanel} this
18369 * @return {Boolean} false if there is an error
18372 'beforedeactivate': true
18375 this.tabId = this.tabId || Roo.id();
18379 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18387 getAutoCreate : function(){
18390 // item is needed for carousel - not sure if it has any effect otherwise
18391 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18392 html: this.html || ''
18396 cfg.cls += ' active';
18400 cfg.tabId = this.tabId;
18407 initEvents: function()
18409 var p = this.parent();
18411 this.navId = this.navId || p.navId;
18413 if (typeof(this.navId) != 'undefined') {
18414 // not really needed.. but just in case.. parent should be a NavGroup.
18415 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18419 var i = tg.tabs.length - 1;
18421 if(this.active && tg.bullets > 0 && i < tg.bullets){
18422 tg.setActiveBullet(i);
18426 this.el.on('click', this.onClick, this);
18429 this.el.on("touchstart", this.onTouchStart, this);
18430 this.el.on("touchmove", this.onTouchMove, this);
18431 this.el.on("touchend", this.onTouchEnd, this);
18436 onRender : function(ct, position)
18438 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18441 setActive : function(state)
18443 Roo.log("panel - set active " + this.tabId + "=" + state);
18445 this.active = state;
18447 this.el.removeClass('active');
18449 } else if (!this.el.hasClass('active')) {
18450 this.el.addClass('active');
18453 this.fireEvent('changed', this, state);
18456 onClick : function(e)
18458 e.preventDefault();
18460 if(!this.href.length){
18464 window.location.href = this.href;
18473 onTouchStart : function(e)
18475 this.swiping = false;
18477 this.startX = e.browserEvent.touches[0].clientX;
18478 this.startY = e.browserEvent.touches[0].clientY;
18481 onTouchMove : function(e)
18483 this.swiping = true;
18485 this.endX = e.browserEvent.touches[0].clientX;
18486 this.endY = e.browserEvent.touches[0].clientY;
18489 onTouchEnd : function(e)
18496 var tabGroup = this.parent();
18498 if(this.endX > this.startX){ // swiping right
18499 tabGroup.showPanelPrev();
18503 if(this.startX > this.endX){ // swiping left
18504 tabGroup.showPanelNext();
18523 * @class Roo.bootstrap.DateField
18524 * @extends Roo.bootstrap.Input
18525 * Bootstrap DateField class
18526 * @cfg {Number} weekStart default 0
18527 * @cfg {String} viewMode default empty, (months|years)
18528 * @cfg {String} minViewMode default empty, (months|years)
18529 * @cfg {Number} startDate default -Infinity
18530 * @cfg {Number} endDate default Infinity
18531 * @cfg {Boolean} todayHighlight default false
18532 * @cfg {Boolean} todayBtn default false
18533 * @cfg {Boolean} calendarWeeks default false
18534 * @cfg {Object} daysOfWeekDisabled default empty
18535 * @cfg {Boolean} singleMode default false (true | false)
18537 * @cfg {Boolean} keyboardNavigation default true
18538 * @cfg {String} language default en
18541 * Create a new DateField
18542 * @param {Object} config The config object
18545 Roo.bootstrap.DateField = function(config){
18546 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18550 * Fires when this field show.
18551 * @param {Roo.bootstrap.DateField} this
18552 * @param {Mixed} date The date value
18557 * Fires when this field hide.
18558 * @param {Roo.bootstrap.DateField} this
18559 * @param {Mixed} date The date value
18564 * Fires when select a date.
18565 * @param {Roo.bootstrap.DateField} this
18566 * @param {Mixed} date The date value
18570 * @event beforeselect
18571 * Fires when before select a date.
18572 * @param {Roo.bootstrap.DateField} this
18573 * @param {Mixed} date The date value
18575 beforeselect : true
18579 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18582 * @cfg {String} format
18583 * The default date format string which can be overriden for localization support. The format must be
18584 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18588 * @cfg {String} altFormats
18589 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18590 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18592 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18600 todayHighlight : false,
18606 keyboardNavigation: true,
18608 calendarWeeks: false,
18610 startDate: -Infinity,
18614 daysOfWeekDisabled: [],
18618 singleMode : false,
18620 UTCDate: function()
18622 return new Date(Date.UTC.apply(Date, arguments));
18625 UTCToday: function()
18627 var today = new Date();
18628 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18631 getDate: function() {
18632 var d = this.getUTCDate();
18633 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18636 getUTCDate: function() {
18640 setDate: function(d) {
18641 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18644 setUTCDate: function(d) {
18646 this.setValue(this.formatDate(this.date));
18649 onRender: function(ct, position)
18652 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18654 this.language = this.language || 'en';
18655 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18656 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18658 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18659 this.format = this.format || 'm/d/y';
18660 this.isInline = false;
18661 this.isInput = true;
18662 this.component = this.el.select('.add-on', true).first() || false;
18663 this.component = (this.component && this.component.length === 0) ? false : this.component;
18664 this.hasInput = this.component && this.inputEl().length;
18666 if (typeof(this.minViewMode === 'string')) {
18667 switch (this.minViewMode) {
18669 this.minViewMode = 1;
18672 this.minViewMode = 2;
18675 this.minViewMode = 0;
18680 if (typeof(this.viewMode === 'string')) {
18681 switch (this.viewMode) {
18694 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18696 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18698 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18700 this.picker().on('mousedown', this.onMousedown, this);
18701 this.picker().on('click', this.onClick, this);
18703 this.picker().addClass('datepicker-dropdown');
18705 this.startViewMode = this.viewMode;
18707 if(this.singleMode){
18708 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18709 v.setVisibilityMode(Roo.Element.DISPLAY);
18713 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18714 v.setStyle('width', '189px');
18718 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18719 if(!this.calendarWeeks){
18724 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18725 v.attr('colspan', function(i, val){
18726 return parseInt(val) + 1;
18731 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18733 this.setStartDate(this.startDate);
18734 this.setEndDate(this.endDate);
18736 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18743 if(this.isInline) {
18748 picker : function()
18750 return this.pickerEl;
18751 // return this.el.select('.datepicker', true).first();
18754 fillDow: function()
18756 var dowCnt = this.weekStart;
18765 if(this.calendarWeeks){
18773 while (dowCnt < this.weekStart + 7) {
18777 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18781 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18784 fillMonths: function()
18787 var months = this.picker().select('>.datepicker-months td', true).first();
18789 months.dom.innerHTML = '';
18795 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18798 months.createChild(month);
18805 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;
18807 if (this.date < this.startDate) {
18808 this.viewDate = new Date(this.startDate);
18809 } else if (this.date > this.endDate) {
18810 this.viewDate = new Date(this.endDate);
18812 this.viewDate = new Date(this.date);
18820 var d = new Date(this.viewDate),
18821 year = d.getUTCFullYear(),
18822 month = d.getUTCMonth(),
18823 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18824 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18825 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18826 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18827 currentDate = this.date && this.date.valueOf(),
18828 today = this.UTCToday();
18830 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18832 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18834 // this.picker.select('>tfoot th.today').
18835 // .text(dates[this.language].today)
18836 // .toggle(this.todayBtn !== false);
18838 this.updateNavArrows();
18841 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18843 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18845 prevMonth.setUTCDate(day);
18847 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18849 var nextMonth = new Date(prevMonth);
18851 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18853 nextMonth = nextMonth.valueOf();
18855 var fillMonths = false;
18857 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18859 while(prevMonth.valueOf() <= nextMonth) {
18862 if (prevMonth.getUTCDay() === this.weekStart) {
18864 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18872 if(this.calendarWeeks){
18873 // ISO 8601: First week contains first thursday.
18874 // ISO also states week starts on Monday, but we can be more abstract here.
18876 // Start of current week: based on weekstart/current date
18877 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18878 // Thursday of this week
18879 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18880 // First Thursday of year, year from thursday
18881 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18882 // Calendar week: ms between thursdays, div ms per day, div 7 days
18883 calWeek = (th - yth) / 864e5 / 7 + 1;
18885 fillMonths.cn.push({
18893 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18895 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18898 if (this.todayHighlight &&
18899 prevMonth.getUTCFullYear() == today.getFullYear() &&
18900 prevMonth.getUTCMonth() == today.getMonth() &&
18901 prevMonth.getUTCDate() == today.getDate()) {
18902 clsName += ' today';
18905 if (currentDate && prevMonth.valueOf() === currentDate) {
18906 clsName += ' active';
18909 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18910 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18911 clsName += ' disabled';
18914 fillMonths.cn.push({
18916 cls: 'day ' + clsName,
18917 html: prevMonth.getDate()
18920 prevMonth.setDate(prevMonth.getDate()+1);
18923 var currentYear = this.date && this.date.getUTCFullYear();
18924 var currentMonth = this.date && this.date.getUTCMonth();
18926 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18928 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18929 v.removeClass('active');
18931 if(currentYear === year && k === currentMonth){
18932 v.addClass('active');
18935 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18936 v.addClass('disabled');
18942 year = parseInt(year/10, 10) * 10;
18944 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18946 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18949 for (var i = -1; i < 11; i++) {
18950 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18952 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18960 showMode: function(dir)
18963 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18966 Roo.each(this.picker().select('>div',true).elements, function(v){
18967 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18970 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18975 if(this.isInline) {
18979 this.picker().removeClass(['bottom', 'top']);
18981 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18983 * place to the top of element!
18987 this.picker().addClass('top');
18988 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18993 this.picker().addClass('bottom');
18995 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18998 parseDate : function(value)
19000 if(!value || value instanceof Date){
19003 var v = Date.parseDate(value, this.format);
19004 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19005 v = Date.parseDate(value, 'Y-m-d');
19007 if(!v && this.altFormats){
19008 if(!this.altFormatsArray){
19009 this.altFormatsArray = this.altFormats.split("|");
19011 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19012 v = Date.parseDate(value, this.altFormatsArray[i]);
19018 formatDate : function(date, fmt)
19020 return (!date || !(date instanceof Date)) ?
19021 date : date.dateFormat(fmt || this.format);
19024 onFocus : function()
19026 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19030 onBlur : function()
19032 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19034 var d = this.inputEl().getValue();
19041 showPopup : function()
19043 this.picker().show();
19047 this.fireEvent('showpopup', this, this.date);
19050 hidePopup : function()
19052 if(this.isInline) {
19055 this.picker().hide();
19056 this.viewMode = this.startViewMode;
19059 this.fireEvent('hidepopup', this, this.date);
19063 onMousedown: function(e)
19065 e.stopPropagation();
19066 e.preventDefault();
19071 Roo.bootstrap.DateField.superclass.keyup.call(this);
19075 setValue: function(v)
19077 if(this.fireEvent('beforeselect', this, v) !== false){
19078 var d = new Date(this.parseDate(v) ).clearTime();
19080 if(isNaN(d.getTime())){
19081 this.date = this.viewDate = '';
19082 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19086 v = this.formatDate(d);
19088 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19090 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19094 this.fireEvent('select', this, this.date);
19098 getValue: function()
19100 return this.formatDate(this.date);
19103 fireKey: function(e)
19105 if (!this.picker().isVisible()){
19106 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19112 var dateChanged = false,
19114 newDate, newViewDate;
19119 e.preventDefault();
19123 if (!this.keyboardNavigation) {
19126 dir = e.keyCode == 37 ? -1 : 1;
19129 newDate = this.moveYear(this.date, dir);
19130 newViewDate = this.moveYear(this.viewDate, dir);
19131 } else if (e.shiftKey){
19132 newDate = this.moveMonth(this.date, dir);
19133 newViewDate = this.moveMonth(this.viewDate, dir);
19135 newDate = new Date(this.date);
19136 newDate.setUTCDate(this.date.getUTCDate() + dir);
19137 newViewDate = new Date(this.viewDate);
19138 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19140 if (this.dateWithinRange(newDate)){
19141 this.date = newDate;
19142 this.viewDate = newViewDate;
19143 this.setValue(this.formatDate(this.date));
19145 e.preventDefault();
19146 dateChanged = true;
19151 if (!this.keyboardNavigation) {
19154 dir = e.keyCode == 38 ? -1 : 1;
19156 newDate = this.moveYear(this.date, dir);
19157 newViewDate = this.moveYear(this.viewDate, dir);
19158 } else if (e.shiftKey){
19159 newDate = this.moveMonth(this.date, dir);
19160 newViewDate = this.moveMonth(this.viewDate, dir);
19162 newDate = new Date(this.date);
19163 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19164 newViewDate = new Date(this.viewDate);
19165 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19167 if (this.dateWithinRange(newDate)){
19168 this.date = newDate;
19169 this.viewDate = newViewDate;
19170 this.setValue(this.formatDate(this.date));
19172 e.preventDefault();
19173 dateChanged = true;
19177 this.setValue(this.formatDate(this.date));
19179 e.preventDefault();
19182 this.setValue(this.formatDate(this.date));
19196 onClick: function(e)
19198 e.stopPropagation();
19199 e.preventDefault();
19201 var target = e.getTarget();
19203 if(target.nodeName.toLowerCase() === 'i'){
19204 target = Roo.get(target).dom.parentNode;
19207 var nodeName = target.nodeName;
19208 var className = target.className;
19209 var html = target.innerHTML;
19210 //Roo.log(nodeName);
19212 switch(nodeName.toLowerCase()) {
19214 switch(className) {
19220 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19221 switch(this.viewMode){
19223 this.viewDate = this.moveMonth(this.viewDate, dir);
19227 this.viewDate = this.moveYear(this.viewDate, dir);
19233 var date = new Date();
19234 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19236 this.setValue(this.formatDate(this.date));
19243 if (className.indexOf('disabled') < 0) {
19244 this.viewDate.setUTCDate(1);
19245 if (className.indexOf('month') > -1) {
19246 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19248 var year = parseInt(html, 10) || 0;
19249 this.viewDate.setUTCFullYear(year);
19253 if(this.singleMode){
19254 this.setValue(this.formatDate(this.viewDate));
19265 //Roo.log(className);
19266 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19267 var day = parseInt(html, 10) || 1;
19268 var year = this.viewDate.getUTCFullYear(),
19269 month = this.viewDate.getUTCMonth();
19271 if (className.indexOf('old') > -1) {
19278 } else if (className.indexOf('new') > -1) {
19286 //Roo.log([year,month,day]);
19287 this.date = this.UTCDate(year, month, day,0,0,0,0);
19288 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19290 //Roo.log(this.formatDate(this.date));
19291 this.setValue(this.formatDate(this.date));
19298 setStartDate: function(startDate)
19300 this.startDate = startDate || -Infinity;
19301 if (this.startDate !== -Infinity) {
19302 this.startDate = this.parseDate(this.startDate);
19305 this.updateNavArrows();
19308 setEndDate: function(endDate)
19310 this.endDate = endDate || Infinity;
19311 if (this.endDate !== Infinity) {
19312 this.endDate = this.parseDate(this.endDate);
19315 this.updateNavArrows();
19318 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19320 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19321 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19322 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19324 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19325 return parseInt(d, 10);
19328 this.updateNavArrows();
19331 updateNavArrows: function()
19333 if(this.singleMode){
19337 var d = new Date(this.viewDate),
19338 year = d.getUTCFullYear(),
19339 month = d.getUTCMonth();
19341 Roo.each(this.picker().select('.prev', true).elements, function(v){
19343 switch (this.viewMode) {
19346 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19352 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19359 Roo.each(this.picker().select('.next', true).elements, function(v){
19361 switch (this.viewMode) {
19364 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19370 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19378 moveMonth: function(date, dir)
19383 var new_date = new Date(date.valueOf()),
19384 day = new_date.getUTCDate(),
19385 month = new_date.getUTCMonth(),
19386 mag = Math.abs(dir),
19388 dir = dir > 0 ? 1 : -1;
19391 // If going back one month, make sure month is not current month
19392 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19394 return new_date.getUTCMonth() == month;
19396 // If going forward one month, make sure month is as expected
19397 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19399 return new_date.getUTCMonth() != new_month;
19401 new_month = month + dir;
19402 new_date.setUTCMonth(new_month);
19403 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19404 if (new_month < 0 || new_month > 11) {
19405 new_month = (new_month + 12) % 12;
19408 // For magnitudes >1, move one month at a time...
19409 for (var i=0; i<mag; i++) {
19410 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19411 new_date = this.moveMonth(new_date, dir);
19413 // ...then reset the day, keeping it in the new month
19414 new_month = new_date.getUTCMonth();
19415 new_date.setUTCDate(day);
19417 return new_month != new_date.getUTCMonth();
19420 // Common date-resetting loop -- if date is beyond end of month, make it
19423 new_date.setUTCDate(--day);
19424 new_date.setUTCMonth(new_month);
19429 moveYear: function(date, dir)
19431 return this.moveMonth(date, dir*12);
19434 dateWithinRange: function(date)
19436 return date >= this.startDate && date <= this.endDate;
19442 this.picker().remove();
19445 validateValue : function(value)
19447 if(this.getVisibilityEl().hasClass('hidden')){
19451 if(value.length < 1) {
19452 if(this.allowBlank){
19458 if(value.length < this.minLength){
19461 if(value.length > this.maxLength){
19465 var vt = Roo.form.VTypes;
19466 if(!vt[this.vtype](value, this)){
19470 if(typeof this.validator == "function"){
19471 var msg = this.validator(value);
19477 if(this.regex && !this.regex.test(value)){
19481 if(typeof(this.parseDate(value)) == 'undefined'){
19485 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19489 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19499 this.date = this.viewDate = '';
19501 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19506 Roo.apply(Roo.bootstrap.DateField, {
19517 html: '<i class="fa fa-arrow-left"/>'
19527 html: '<i class="fa fa-arrow-right"/>'
19569 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19570 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19571 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19572 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19573 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19586 navFnc: 'FullYear',
19591 navFnc: 'FullYear',
19596 Roo.apply(Roo.bootstrap.DateField, {
19600 cls: 'datepicker dropdown-menu roo-dynamic',
19604 cls: 'datepicker-days',
19608 cls: 'table-condensed',
19610 Roo.bootstrap.DateField.head,
19614 Roo.bootstrap.DateField.footer
19621 cls: 'datepicker-months',
19625 cls: 'table-condensed',
19627 Roo.bootstrap.DateField.head,
19628 Roo.bootstrap.DateField.content,
19629 Roo.bootstrap.DateField.footer
19636 cls: 'datepicker-years',
19640 cls: 'table-condensed',
19642 Roo.bootstrap.DateField.head,
19643 Roo.bootstrap.DateField.content,
19644 Roo.bootstrap.DateField.footer
19663 * @class Roo.bootstrap.TimeField
19664 * @extends Roo.bootstrap.Input
19665 * Bootstrap DateField class
19669 * Create a new TimeField
19670 * @param {Object} config The config object
19673 Roo.bootstrap.TimeField = function(config){
19674 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19678 * Fires when this field show.
19679 * @param {Roo.bootstrap.DateField} thisthis
19680 * @param {Mixed} date The date value
19685 * Fires when this field hide.
19686 * @param {Roo.bootstrap.DateField} this
19687 * @param {Mixed} date The date value
19692 * Fires when select a date.
19693 * @param {Roo.bootstrap.DateField} this
19694 * @param {Mixed} date The date value
19700 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19703 * @cfg {String} format
19704 * The default time format string which can be overriden for localization support. The format must be
19705 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19709 onRender: function(ct, position)
19712 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19714 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19716 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19718 this.pop = this.picker().select('>.datepicker-time',true).first();
19719 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19721 this.picker().on('mousedown', this.onMousedown, this);
19722 this.picker().on('click', this.onClick, this);
19724 this.picker().addClass('datepicker-dropdown');
19729 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19730 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19731 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19732 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19733 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19734 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19738 fireKey: function(e){
19739 if (!this.picker().isVisible()){
19740 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19746 e.preventDefault();
19754 this.onTogglePeriod();
19757 this.onIncrementMinutes();
19760 this.onDecrementMinutes();
19769 onClick: function(e) {
19770 e.stopPropagation();
19771 e.preventDefault();
19774 picker : function()
19776 return this.el.select('.datepicker', true).first();
19779 fillTime: function()
19781 var time = this.pop.select('tbody', true).first();
19783 time.dom.innerHTML = '';
19798 cls: 'hours-up glyphicon glyphicon-chevron-up'
19818 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19839 cls: 'timepicker-hour',
19854 cls: 'timepicker-minute',
19869 cls: 'btn btn-primary period',
19891 cls: 'hours-down glyphicon glyphicon-chevron-down'
19911 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19929 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19936 var hours = this.time.getHours();
19937 var minutes = this.time.getMinutes();
19950 hours = hours - 12;
19954 hours = '0' + hours;
19958 minutes = '0' + minutes;
19961 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19962 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19963 this.pop.select('button', true).first().dom.innerHTML = period;
19969 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19971 var cls = ['bottom'];
19973 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19980 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19985 this.picker().addClass(cls.join('-'));
19989 Roo.each(cls, function(c){
19991 _this.picker().setTop(_this.inputEl().getHeight());
19995 _this.picker().setTop(0 - _this.picker().getHeight());
20000 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20004 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20011 onFocus : function()
20013 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20017 onBlur : function()
20019 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20025 this.picker().show();
20030 this.fireEvent('show', this, this.date);
20035 this.picker().hide();
20038 this.fireEvent('hide', this, this.date);
20041 setTime : function()
20044 this.setValue(this.time.format(this.format));
20046 this.fireEvent('select', this, this.date);
20051 onMousedown: function(e){
20052 e.stopPropagation();
20053 e.preventDefault();
20056 onIncrementHours: function()
20058 Roo.log('onIncrementHours');
20059 this.time = this.time.add(Date.HOUR, 1);
20064 onDecrementHours: function()
20066 Roo.log('onDecrementHours');
20067 this.time = this.time.add(Date.HOUR, -1);
20071 onIncrementMinutes: function()
20073 Roo.log('onIncrementMinutes');
20074 this.time = this.time.add(Date.MINUTE, 1);
20078 onDecrementMinutes: function()
20080 Roo.log('onDecrementMinutes');
20081 this.time = this.time.add(Date.MINUTE, -1);
20085 onTogglePeriod: function()
20087 Roo.log('onTogglePeriod');
20088 this.time = this.time.add(Date.HOUR, 12);
20095 Roo.apply(Roo.bootstrap.TimeField, {
20125 cls: 'btn btn-info ok',
20137 Roo.apply(Roo.bootstrap.TimeField, {
20141 cls: 'datepicker dropdown-menu',
20145 cls: 'datepicker-time',
20149 cls: 'table-condensed',
20151 Roo.bootstrap.TimeField.content,
20152 Roo.bootstrap.TimeField.footer
20171 * @class Roo.bootstrap.MonthField
20172 * @extends Roo.bootstrap.Input
20173 * Bootstrap MonthField class
20175 * @cfg {String} language default en
20178 * Create a new MonthField
20179 * @param {Object} config The config object
20182 Roo.bootstrap.MonthField = function(config){
20183 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20188 * Fires when this field show.
20189 * @param {Roo.bootstrap.MonthField} this
20190 * @param {Mixed} date The date value
20195 * Fires when this field hide.
20196 * @param {Roo.bootstrap.MonthField} this
20197 * @param {Mixed} date The date value
20202 * Fires when select a date.
20203 * @param {Roo.bootstrap.MonthField} this
20204 * @param {String} oldvalue The old value
20205 * @param {String} newvalue The new value
20211 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20213 onRender: function(ct, position)
20216 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20218 this.language = this.language || 'en';
20219 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20220 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20222 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20223 this.isInline = false;
20224 this.isInput = true;
20225 this.component = this.el.select('.add-on', true).first() || false;
20226 this.component = (this.component && this.component.length === 0) ? false : this.component;
20227 this.hasInput = this.component && this.inputEL().length;
20229 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20231 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20233 this.picker().on('mousedown', this.onMousedown, this);
20234 this.picker().on('click', this.onClick, this);
20236 this.picker().addClass('datepicker-dropdown');
20238 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20239 v.setStyle('width', '189px');
20246 if(this.isInline) {
20252 setValue: function(v, suppressEvent)
20254 var o = this.getValue();
20256 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20260 if(suppressEvent !== true){
20261 this.fireEvent('select', this, o, v);
20266 getValue: function()
20271 onClick: function(e)
20273 e.stopPropagation();
20274 e.preventDefault();
20276 var target = e.getTarget();
20278 if(target.nodeName.toLowerCase() === 'i'){
20279 target = Roo.get(target).dom.parentNode;
20282 var nodeName = target.nodeName;
20283 var className = target.className;
20284 var html = target.innerHTML;
20286 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20290 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20292 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20298 picker : function()
20300 return this.pickerEl;
20303 fillMonths: function()
20306 var months = this.picker().select('>.datepicker-months td', true).first();
20308 months.dom.innerHTML = '';
20314 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20317 months.createChild(month);
20326 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20327 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20330 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20331 e.removeClass('active');
20333 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20334 e.addClass('active');
20341 if(this.isInline) {
20345 this.picker().removeClass(['bottom', 'top']);
20347 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20349 * place to the top of element!
20353 this.picker().addClass('top');
20354 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20359 this.picker().addClass('bottom');
20361 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20364 onFocus : function()
20366 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20370 onBlur : function()
20372 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20374 var d = this.inputEl().getValue();
20383 this.picker().show();
20384 this.picker().select('>.datepicker-months', true).first().show();
20388 this.fireEvent('show', this, this.date);
20393 if(this.isInline) {
20396 this.picker().hide();
20397 this.fireEvent('hide', this, this.date);
20401 onMousedown: function(e)
20403 e.stopPropagation();
20404 e.preventDefault();
20409 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20413 fireKey: function(e)
20415 if (!this.picker().isVisible()){
20416 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20427 e.preventDefault();
20431 dir = e.keyCode == 37 ? -1 : 1;
20433 this.vIndex = this.vIndex + dir;
20435 if(this.vIndex < 0){
20439 if(this.vIndex > 11){
20443 if(isNaN(this.vIndex)){
20447 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20453 dir = e.keyCode == 38 ? -1 : 1;
20455 this.vIndex = this.vIndex + dir * 4;
20457 if(this.vIndex < 0){
20461 if(this.vIndex > 11){
20465 if(isNaN(this.vIndex)){
20469 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20474 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20475 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20479 e.preventDefault();
20482 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20483 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20499 this.picker().remove();
20504 Roo.apply(Roo.bootstrap.MonthField, {
20523 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20524 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20529 Roo.apply(Roo.bootstrap.MonthField, {
20533 cls: 'datepicker dropdown-menu roo-dynamic',
20537 cls: 'datepicker-months',
20541 cls: 'table-condensed',
20543 Roo.bootstrap.DateField.content
20563 * @class Roo.bootstrap.CheckBox
20564 * @extends Roo.bootstrap.Input
20565 * Bootstrap CheckBox class
20567 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20568 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20569 * @cfg {String} boxLabel The text that appears beside the checkbox
20570 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20571 * @cfg {Boolean} checked initnal the element
20572 * @cfg {Boolean} inline inline the element (default false)
20573 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20574 * @cfg {String} tooltip label tooltip
20577 * Create a new CheckBox
20578 * @param {Object} config The config object
20581 Roo.bootstrap.CheckBox = function(config){
20582 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20587 * Fires when the element is checked or unchecked.
20588 * @param {Roo.bootstrap.CheckBox} this This input
20589 * @param {Boolean} checked The new checked value
20594 * Fires when the element is click.
20595 * @param {Roo.bootstrap.CheckBox} this This input
20602 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20604 inputType: 'checkbox',
20613 getAutoCreate : function()
20615 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20621 cfg.cls = 'form-group ' + this.inputType; //input-group
20624 cfg.cls += ' ' + this.inputType + '-inline';
20630 type : this.inputType,
20631 value : this.inputValue,
20632 cls : 'roo-' + this.inputType, //'form-box',
20633 placeholder : this.placeholder || ''
20637 if(this.inputType != 'radio'){
20641 cls : 'roo-hidden-value',
20642 value : this.checked ? this.inputValue : this.valueOff
20647 if (this.weight) { // Validity check?
20648 cfg.cls += " " + this.inputType + "-" + this.weight;
20651 if (this.disabled) {
20652 input.disabled=true;
20656 input.checked = this.checked;
20661 input.name = this.name;
20663 if(this.inputType != 'radio'){
20664 hidden.name = this.name;
20665 input.name = '_hidden_' + this.name;
20670 input.cls += ' input-' + this.size;
20675 ['xs','sm','md','lg'].map(function(size){
20676 if (settings[size]) {
20677 cfg.cls += ' col-' + size + '-' + settings[size];
20681 var inputblock = input;
20683 if (this.before || this.after) {
20686 cls : 'input-group',
20691 inputblock.cn.push({
20693 cls : 'input-group-addon',
20698 inputblock.cn.push(input);
20700 if(this.inputType != 'radio'){
20701 inputblock.cn.push(hidden);
20705 inputblock.cn.push({
20707 cls : 'input-group-addon',
20714 if (align ==='left' && this.fieldLabel.length) {
20715 // Roo.log("left and has label");
20720 cls : 'control-label',
20721 html : this.fieldLabel
20731 if(this.labelWidth > 12){
20732 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20735 if(this.labelWidth < 13 && this.labelmd == 0){
20736 this.labelmd = this.labelWidth;
20739 if(this.labellg > 0){
20740 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20741 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20744 if(this.labelmd > 0){
20745 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20746 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20749 if(this.labelsm > 0){
20750 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20751 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20754 if(this.labelxs > 0){
20755 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20756 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20759 } else if ( this.fieldLabel.length) {
20760 // Roo.log(" label");
20764 tag: this.boxLabel ? 'span' : 'label',
20766 cls: 'control-label box-input-label',
20767 //cls : 'input-group-addon',
20768 html : this.fieldLabel
20777 // Roo.log(" no label && no align");
20778 cfg.cn = [ inputblock ] ;
20784 var boxLabelCfg = {
20786 //'for': id, // box label is handled by onclick - so no for...
20788 html: this.boxLabel
20792 boxLabelCfg.tooltip = this.tooltip;
20795 cfg.cn.push(boxLabelCfg);
20798 if(this.inputType != 'radio'){
20799 cfg.cn.push(hidden);
20807 * return the real input element.
20809 inputEl: function ()
20811 return this.el.select('input.roo-' + this.inputType,true).first();
20813 hiddenEl: function ()
20815 return this.el.select('input.roo-hidden-value',true).first();
20818 labelEl: function()
20820 return this.el.select('label.control-label',true).first();
20822 /* depricated... */
20826 return this.labelEl();
20829 boxLabelEl: function()
20831 return this.el.select('label.box-label',true).first();
20834 initEvents : function()
20836 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20838 this.inputEl().on('click', this.onClick, this);
20840 if (this.boxLabel) {
20841 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20844 this.startValue = this.getValue();
20847 Roo.bootstrap.CheckBox.register(this);
20851 onClick : function(e)
20853 if(this.fireEvent('click', this, e) !== false){
20854 this.setChecked(!this.checked);
20859 setChecked : function(state,suppressEvent)
20861 this.startValue = this.getValue();
20863 if(this.inputType == 'radio'){
20865 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20866 e.dom.checked = false;
20869 this.inputEl().dom.checked = true;
20871 this.inputEl().dom.value = this.inputValue;
20873 if(suppressEvent !== true){
20874 this.fireEvent('check', this, true);
20882 this.checked = state;
20884 this.inputEl().dom.checked = state;
20887 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20889 if(suppressEvent !== true){
20890 this.fireEvent('check', this, state);
20896 getValue : function()
20898 if(this.inputType == 'radio'){
20899 return this.getGroupValue();
20902 return this.hiddenEl().dom.value;
20906 getGroupValue : function()
20908 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20912 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20915 setValue : function(v,suppressEvent)
20917 if(this.inputType == 'radio'){
20918 this.setGroupValue(v, suppressEvent);
20922 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20927 setGroupValue : function(v, suppressEvent)
20929 this.startValue = this.getValue();
20931 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20932 e.dom.checked = false;
20934 if(e.dom.value == v){
20935 e.dom.checked = true;
20939 if(suppressEvent !== true){
20940 this.fireEvent('check', this, true);
20948 validate : function()
20950 if(this.getVisibilityEl().hasClass('hidden')){
20956 (this.inputType == 'radio' && this.validateRadio()) ||
20957 (this.inputType == 'checkbox' && this.validateCheckbox())
20963 this.markInvalid();
20967 validateRadio : function()
20969 if(this.getVisibilityEl().hasClass('hidden')){
20973 if(this.allowBlank){
20979 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20980 if(!e.dom.checked){
20992 validateCheckbox : function()
20995 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20996 //return (this.getValue() == this.inputValue) ? true : false;
20999 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21007 for(var i in group){
21008 if(group[i].el.isVisible(true)){
21016 for(var i in group){
21021 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21028 * Mark this field as valid
21030 markValid : function()
21034 this.fireEvent('valid', this);
21036 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21039 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21046 if(this.inputType == 'radio'){
21047 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21048 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21049 e.findParent('.form-group', false, true).addClass(_this.validClass);
21056 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21057 this.el.findParent('.form-group', false, true).addClass(this.validClass);
21061 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21067 for(var i in group){
21068 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21069 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21074 * Mark this field as invalid
21075 * @param {String} msg The validation message
21077 markInvalid : function(msg)
21079 if(this.allowBlank){
21085 this.fireEvent('invalid', this, msg);
21087 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21090 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21094 label.markInvalid();
21097 if(this.inputType == 'radio'){
21098 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21099 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21100 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21107 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21108 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21112 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21118 for(var i in group){
21119 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21120 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21125 clearInvalid : function()
21127 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21129 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21131 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21133 if (label && label.iconEl) {
21134 label.iconEl.removeClass(label.validClass);
21135 label.iconEl.removeClass(label.invalidClass);
21139 disable : function()
21141 if(this.inputType != 'radio'){
21142 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21149 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21150 _this.getActionEl().addClass(this.disabledClass);
21151 e.dom.disabled = true;
21155 this.disabled = true;
21156 this.fireEvent("disable", this);
21160 enable : function()
21162 if(this.inputType != 'radio'){
21163 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21170 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21171 _this.getActionEl().removeClass(this.disabledClass);
21172 e.dom.disabled = false;
21176 this.disabled = false;
21177 this.fireEvent("enable", this);
21181 setBoxLabel : function(v)
21186 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21192 Roo.apply(Roo.bootstrap.CheckBox, {
21197 * register a CheckBox Group
21198 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21200 register : function(checkbox)
21202 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21203 this.groups[checkbox.groupId] = {};
21206 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21210 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21214 * fetch a CheckBox Group based on the group ID
21215 * @param {string} the group ID
21216 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21218 get: function(groupId) {
21219 if (typeof(this.groups[groupId]) == 'undefined') {
21223 return this.groups[groupId] ;
21236 * @class Roo.bootstrap.Radio
21237 * @extends Roo.bootstrap.Component
21238 * Bootstrap Radio class
21239 * @cfg {String} boxLabel - the label associated
21240 * @cfg {String} value - the value of radio
21243 * Create a new Radio
21244 * @param {Object} config The config object
21246 Roo.bootstrap.Radio = function(config){
21247 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21251 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21257 getAutoCreate : function()
21261 cls : 'form-group radio',
21266 html : this.boxLabel
21274 initEvents : function()
21276 this.parent().register(this);
21278 this.el.on('click', this.onClick, this);
21282 onClick : function(e)
21284 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21285 this.setChecked(true);
21289 setChecked : function(state, suppressEvent)
21291 this.parent().setValue(this.value, suppressEvent);
21295 setBoxLabel : function(v)
21300 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21315 * @class Roo.bootstrap.SecurePass
21316 * @extends Roo.bootstrap.Input
21317 * Bootstrap SecurePass class
21321 * Create a new SecurePass
21322 * @param {Object} config The config object
21325 Roo.bootstrap.SecurePass = function (config) {
21326 // these go here, so the translation tool can replace them..
21328 PwdEmpty: "Please type a password, and then retype it to confirm.",
21329 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21330 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21331 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21332 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21333 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21334 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21335 TooWeak: "Your password is Too Weak."
21337 this.meterLabel = "Password strength:";
21338 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21339 this.meterClass = [
21340 "roo-password-meter-tooweak",
21341 "roo-password-meter-weak",
21342 "roo-password-meter-medium",
21343 "roo-password-meter-strong",
21344 "roo-password-meter-grey"
21349 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21352 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21354 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21356 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21357 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21358 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21359 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21360 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21361 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21362 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21372 * @cfg {String/Object} Label for the strength meter (defaults to
21373 * 'Password strength:')
21378 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21379 * ['Weak', 'Medium', 'Strong'])
21382 pwdStrengths: false,
21395 initEvents: function ()
21397 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21399 if (this.el.is('input[type=password]') && Roo.isSafari) {
21400 this.el.on('keydown', this.SafariOnKeyDown, this);
21403 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21406 onRender: function (ct, position)
21408 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21409 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21410 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21412 this.trigger.createChild({
21417 cls: 'roo-password-meter-grey col-xs-12',
21420 //width: this.meterWidth + 'px'
21424 cls: 'roo-password-meter-text'
21430 if (this.hideTrigger) {
21431 this.trigger.setDisplayed(false);
21433 this.setSize(this.width || '', this.height || '');
21436 onDestroy: function ()
21438 if (this.trigger) {
21439 this.trigger.removeAllListeners();
21440 this.trigger.remove();
21443 this.wrap.remove();
21445 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21448 checkStrength: function ()
21450 var pwd = this.inputEl().getValue();
21451 if (pwd == this._lastPwd) {
21456 if (this.ClientSideStrongPassword(pwd)) {
21458 } else if (this.ClientSideMediumPassword(pwd)) {
21460 } else if (this.ClientSideWeakPassword(pwd)) {
21466 Roo.log('strength1: ' + strength);
21468 //var pm = this.trigger.child('div/div/div').dom;
21469 var pm = this.trigger.child('div/div');
21470 pm.removeClass(this.meterClass);
21471 pm.addClass(this.meterClass[strength]);
21474 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21476 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21478 this._lastPwd = pwd;
21482 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21484 this._lastPwd = '';
21486 var pm = this.trigger.child('div/div');
21487 pm.removeClass(this.meterClass);
21488 pm.addClass('roo-password-meter-grey');
21491 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21494 this.inputEl().dom.type='password';
21497 validateValue: function (value)
21500 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21503 if (value.length == 0) {
21504 if (this.allowBlank) {
21505 this.clearInvalid();
21509 this.markInvalid(this.errors.PwdEmpty);
21510 this.errorMsg = this.errors.PwdEmpty;
21518 if ('[\x21-\x7e]*'.match(value)) {
21519 this.markInvalid(this.errors.PwdBadChar);
21520 this.errorMsg = this.errors.PwdBadChar;
21523 if (value.length < 6) {
21524 this.markInvalid(this.errors.PwdShort);
21525 this.errorMsg = this.errors.PwdShort;
21528 if (value.length > 16) {
21529 this.markInvalid(this.errors.PwdLong);
21530 this.errorMsg = this.errors.PwdLong;
21534 if (this.ClientSideStrongPassword(value)) {
21536 } else if (this.ClientSideMediumPassword(value)) {
21538 } else if (this.ClientSideWeakPassword(value)) {
21545 if (strength < 2) {
21546 //this.markInvalid(this.errors.TooWeak);
21547 this.errorMsg = this.errors.TooWeak;
21552 console.log('strength2: ' + strength);
21554 //var pm = this.trigger.child('div/div/div').dom;
21556 var pm = this.trigger.child('div/div');
21557 pm.removeClass(this.meterClass);
21558 pm.addClass(this.meterClass[strength]);
21560 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21562 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21564 this.errorMsg = '';
21568 CharacterSetChecks: function (type)
21571 this.fResult = false;
21574 isctype: function (character, type)
21577 case this.kCapitalLetter:
21578 if (character >= 'A' && character <= 'Z') {
21583 case this.kSmallLetter:
21584 if (character >= 'a' && character <= 'z') {
21590 if (character >= '0' && character <= '9') {
21595 case this.kPunctuation:
21596 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21607 IsLongEnough: function (pwd, size)
21609 return !(pwd == null || isNaN(size) || pwd.length < size);
21612 SpansEnoughCharacterSets: function (word, nb)
21614 if (!this.IsLongEnough(word, nb))
21619 var characterSetChecks = new Array(
21620 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21621 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21624 for (var index = 0; index < word.length; ++index) {
21625 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21626 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21627 characterSetChecks[nCharSet].fResult = true;
21634 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21635 if (characterSetChecks[nCharSet].fResult) {
21640 if (nCharSets < nb) {
21646 ClientSideStrongPassword: function (pwd)
21648 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21651 ClientSideMediumPassword: function (pwd)
21653 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21656 ClientSideWeakPassword: function (pwd)
21658 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21661 })//<script type="text/javascript">
21664 * Based Ext JS Library 1.1.1
21665 * Copyright(c) 2006-2007, Ext JS, LLC.
21671 * @class Roo.HtmlEditorCore
21672 * @extends Roo.Component
21673 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21675 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21678 Roo.HtmlEditorCore = function(config){
21681 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21686 * @event initialize
21687 * Fires when the editor is fully initialized (including the iframe)
21688 * @param {Roo.HtmlEditorCore} this
21693 * Fires when the editor is first receives the focus. Any insertion must wait
21694 * until after this event.
21695 * @param {Roo.HtmlEditorCore} this
21699 * @event beforesync
21700 * Fires before the textarea is updated with content from the editor iframe. Return false
21701 * to cancel the sync.
21702 * @param {Roo.HtmlEditorCore} this
21703 * @param {String} html
21707 * @event beforepush
21708 * Fires before the iframe editor is updated with content from the textarea. Return false
21709 * to cancel the push.
21710 * @param {Roo.HtmlEditorCore} this
21711 * @param {String} html
21716 * Fires when the textarea is updated with content from the editor iframe.
21717 * @param {Roo.HtmlEditorCore} this
21718 * @param {String} html
21723 * Fires when the iframe editor is updated with content from the textarea.
21724 * @param {Roo.HtmlEditorCore} this
21725 * @param {String} html
21730 * @event editorevent
21731 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21732 * @param {Roo.HtmlEditorCore} this
21738 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21740 // defaults : white / black...
21741 this.applyBlacklists();
21748 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21752 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21758 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21763 * @cfg {Number} height (in pixels)
21767 * @cfg {Number} width (in pixels)
21772 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21775 stylesheets: false,
21780 // private properties
21781 validationEvent : false,
21783 initialized : false,
21785 sourceEditMode : false,
21786 onFocus : Roo.emptyFn,
21788 hideMode:'offsets',
21792 // blacklist + whitelisted elements..
21799 * Protected method that will not generally be called directly. It
21800 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21801 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21803 getDocMarkup : function(){
21807 // inherit styels from page...??
21808 if (this.stylesheets === false) {
21810 Roo.get(document.head).select('style').each(function(node) {
21811 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21814 Roo.get(document.head).select('link').each(function(node) {
21815 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21818 } else if (!this.stylesheets.length) {
21820 st = '<style type="text/css">' +
21821 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21824 st = '<style type="text/css">' +
21829 st += '<style type="text/css">' +
21830 'IMG { cursor: pointer } ' +
21833 var cls = 'roo-htmleditor-body';
21835 if(this.bodyCls.length){
21836 cls += ' ' + this.bodyCls;
21839 return '<html><head>' + st +
21840 //<style type="text/css">' +
21841 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21843 ' </head><body class="' + cls + '"></body></html>';
21847 onRender : function(ct, position)
21850 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21851 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21854 this.el.dom.style.border = '0 none';
21855 this.el.dom.setAttribute('tabIndex', -1);
21856 this.el.addClass('x-hidden hide');
21860 if(Roo.isIE){ // fix IE 1px bogus margin
21861 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21865 this.frameId = Roo.id();
21869 var iframe = this.owner.wrap.createChild({
21871 cls: 'form-control', // bootstrap..
21873 name: this.frameId,
21874 frameBorder : 'no',
21875 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21880 this.iframe = iframe.dom;
21882 this.assignDocWin();
21884 this.doc.designMode = 'on';
21887 this.doc.write(this.getDocMarkup());
21891 var task = { // must defer to wait for browser to be ready
21893 //console.log("run task?" + this.doc.readyState);
21894 this.assignDocWin();
21895 if(this.doc.body || this.doc.readyState == 'complete'){
21897 this.doc.designMode="on";
21901 Roo.TaskMgr.stop(task);
21902 this.initEditor.defer(10, this);
21909 Roo.TaskMgr.start(task);
21914 onResize : function(w, h)
21916 Roo.log('resize: ' +w + ',' + h );
21917 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21921 if(typeof w == 'number'){
21923 this.iframe.style.width = w + 'px';
21925 if(typeof h == 'number'){
21927 this.iframe.style.height = h + 'px';
21929 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21936 * Toggles the editor between standard and source edit mode.
21937 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21939 toggleSourceEdit : function(sourceEditMode){
21941 this.sourceEditMode = sourceEditMode === true;
21943 if(this.sourceEditMode){
21945 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21948 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21949 //this.iframe.className = '';
21952 //this.setSize(this.owner.wrap.getSize());
21953 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21960 * Protected method that will not generally be called directly. If you need/want
21961 * custom HTML cleanup, this is the method you should override.
21962 * @param {String} html The HTML to be cleaned
21963 * return {String} The cleaned HTML
21965 cleanHtml : function(html){
21966 html = String(html);
21967 if(html.length > 5){
21968 if(Roo.isSafari){ // strip safari nonsense
21969 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21972 if(html == ' '){
21979 * HTML Editor -> Textarea
21980 * Protected method that will not generally be called directly. Syncs the contents
21981 * of the editor iframe with the textarea.
21983 syncValue : function(){
21984 if(this.initialized){
21985 var bd = (this.doc.body || this.doc.documentElement);
21986 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21987 var html = bd.innerHTML;
21989 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21990 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21992 html = '<div style="'+m[0]+'">' + html + '</div>';
21995 html = this.cleanHtml(html);
21996 // fix up the special chars.. normaly like back quotes in word...
21997 // however we do not want to do this with chinese..
21998 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21999 var cc = b.charCodeAt();
22001 (cc >= 0x4E00 && cc < 0xA000 ) ||
22002 (cc >= 0x3400 && cc < 0x4E00 ) ||
22003 (cc >= 0xf900 && cc < 0xfb00 )
22009 if(this.owner.fireEvent('beforesync', this, html) !== false){
22010 this.el.dom.value = html;
22011 this.owner.fireEvent('sync', this, html);
22017 * Protected method that will not generally be called directly. Pushes the value of the textarea
22018 * into the iframe editor.
22020 pushValue : function(){
22021 if(this.initialized){
22022 var v = this.el.dom.value.trim();
22024 // if(v.length < 1){
22028 if(this.owner.fireEvent('beforepush', this, v) !== false){
22029 var d = (this.doc.body || this.doc.documentElement);
22031 this.cleanUpPaste();
22032 this.el.dom.value = d.innerHTML;
22033 this.owner.fireEvent('push', this, v);
22039 deferFocus : function(){
22040 this.focus.defer(10, this);
22044 focus : function(){
22045 if(this.win && !this.sourceEditMode){
22052 assignDocWin: function()
22054 var iframe = this.iframe;
22057 this.doc = iframe.contentWindow.document;
22058 this.win = iframe.contentWindow;
22060 // if (!Roo.get(this.frameId)) {
22063 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22064 // this.win = Roo.get(this.frameId).dom.contentWindow;
22066 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22070 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22071 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22076 initEditor : function(){
22077 //console.log("INIT EDITOR");
22078 this.assignDocWin();
22082 this.doc.designMode="on";
22084 this.doc.write(this.getDocMarkup());
22087 var dbody = (this.doc.body || this.doc.documentElement);
22088 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22089 // this copies styles from the containing element into thsi one..
22090 // not sure why we need all of this..
22091 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22093 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22094 //ss['background-attachment'] = 'fixed'; // w3c
22095 dbody.bgProperties = 'fixed'; // ie
22096 //Roo.DomHelper.applyStyles(dbody, ss);
22097 Roo.EventManager.on(this.doc, {
22098 //'mousedown': this.onEditorEvent,
22099 'mouseup': this.onEditorEvent,
22100 'dblclick': this.onEditorEvent,
22101 'click': this.onEditorEvent,
22102 'keyup': this.onEditorEvent,
22107 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22109 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22110 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22112 this.initialized = true;
22114 this.owner.fireEvent('initialize', this);
22119 onDestroy : function(){
22125 //for (var i =0; i < this.toolbars.length;i++) {
22126 // // fixme - ask toolbars for heights?
22127 // this.toolbars[i].onDestroy();
22130 //this.wrap.dom.innerHTML = '';
22131 //this.wrap.remove();
22136 onFirstFocus : function(){
22138 this.assignDocWin();
22141 this.activated = true;
22144 if(Roo.isGecko){ // prevent silly gecko errors
22146 var s = this.win.getSelection();
22147 if(!s.focusNode || s.focusNode.nodeType != 3){
22148 var r = s.getRangeAt(0);
22149 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22154 this.execCmd('useCSS', true);
22155 this.execCmd('styleWithCSS', false);
22158 this.owner.fireEvent('activate', this);
22162 adjustFont: function(btn){
22163 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22164 //if(Roo.isSafari){ // safari
22167 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22168 if(Roo.isSafari){ // safari
22169 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22170 v = (v < 10) ? 10 : v;
22171 v = (v > 48) ? 48 : v;
22172 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22177 v = Math.max(1, v+adjust);
22179 this.execCmd('FontSize', v );
22182 onEditorEvent : function(e)
22184 this.owner.fireEvent('editorevent', this, e);
22185 // this.updateToolbar();
22186 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22189 insertTag : function(tg)
22191 // could be a bit smarter... -> wrap the current selected tRoo..
22192 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22194 range = this.createRange(this.getSelection());
22195 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22196 wrappingNode.appendChild(range.extractContents());
22197 range.insertNode(wrappingNode);
22204 this.execCmd("formatblock", tg);
22208 insertText : function(txt)
22212 var range = this.createRange();
22213 range.deleteContents();
22214 //alert(Sender.getAttribute('label'));
22216 range.insertNode(this.doc.createTextNode(txt));
22222 * Executes a Midas editor command on the editor document and performs necessary focus and
22223 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22224 * @param {String} cmd The Midas command
22225 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22227 relayCmd : function(cmd, value){
22229 this.execCmd(cmd, value);
22230 this.owner.fireEvent('editorevent', this);
22231 //this.updateToolbar();
22232 this.owner.deferFocus();
22236 * Executes a Midas editor command directly on the editor document.
22237 * For visual commands, you should use {@link #relayCmd} instead.
22238 * <b>This should only be called after the editor is initialized.</b>
22239 * @param {String} cmd The Midas command
22240 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22242 execCmd : function(cmd, value){
22243 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22250 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22252 * @param {String} text | dom node..
22254 insertAtCursor : function(text)
22257 if(!this.activated){
22263 var r = this.doc.selection.createRange();
22274 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22278 // from jquery ui (MIT licenced)
22280 var win = this.win;
22282 if (win.getSelection && win.getSelection().getRangeAt) {
22283 range = win.getSelection().getRangeAt(0);
22284 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22285 range.insertNode(node);
22286 } else if (win.document.selection && win.document.selection.createRange) {
22287 // no firefox support
22288 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22289 win.document.selection.createRange().pasteHTML(txt);
22291 // no firefox support
22292 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22293 this.execCmd('InsertHTML', txt);
22302 mozKeyPress : function(e){
22304 var c = e.getCharCode(), cmd;
22307 c = String.fromCharCode(c).toLowerCase();
22321 this.cleanUpPaste.defer(100, this);
22329 e.preventDefault();
22337 fixKeys : function(){ // load time branching for fastest keydown performance
22339 return function(e){
22340 var k = e.getKey(), r;
22343 r = this.doc.selection.createRange();
22346 r.pasteHTML('    ');
22353 r = this.doc.selection.createRange();
22355 var target = r.parentElement();
22356 if(!target || target.tagName.toLowerCase() != 'li'){
22358 r.pasteHTML('<br />');
22364 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22365 this.cleanUpPaste.defer(100, this);
22371 }else if(Roo.isOpera){
22372 return function(e){
22373 var k = e.getKey();
22377 this.execCmd('InsertHTML','    ');
22380 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22381 this.cleanUpPaste.defer(100, this);
22386 }else if(Roo.isSafari){
22387 return function(e){
22388 var k = e.getKey();
22392 this.execCmd('InsertText','\t');
22396 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22397 this.cleanUpPaste.defer(100, this);
22405 getAllAncestors: function()
22407 var p = this.getSelectedNode();
22410 a.push(p); // push blank onto stack..
22411 p = this.getParentElement();
22415 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22419 a.push(this.doc.body);
22423 lastSelNode : false,
22426 getSelection : function()
22428 this.assignDocWin();
22429 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22432 getSelectedNode: function()
22434 // this may only work on Gecko!!!
22436 // should we cache this!!!!
22441 var range = this.createRange(this.getSelection()).cloneRange();
22444 var parent = range.parentElement();
22446 var testRange = range.duplicate();
22447 testRange.moveToElementText(parent);
22448 if (testRange.inRange(range)) {
22451 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22454 parent = parent.parentElement;
22459 // is ancestor a text element.
22460 var ac = range.commonAncestorContainer;
22461 if (ac.nodeType == 3) {
22462 ac = ac.parentNode;
22465 var ar = ac.childNodes;
22468 var other_nodes = [];
22469 var has_other_nodes = false;
22470 for (var i=0;i<ar.length;i++) {
22471 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22474 // fullly contained node.
22476 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22481 // probably selected..
22482 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22483 other_nodes.push(ar[i]);
22487 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22492 has_other_nodes = true;
22494 if (!nodes.length && other_nodes.length) {
22495 nodes= other_nodes;
22497 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22503 createRange: function(sel)
22505 // this has strange effects when using with
22506 // top toolbar - not sure if it's a great idea.
22507 //this.editor.contentWindow.focus();
22508 if (typeof sel != "undefined") {
22510 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22512 return this.doc.createRange();
22515 return this.doc.createRange();
22518 getParentElement: function()
22521 this.assignDocWin();
22522 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22524 var range = this.createRange(sel);
22527 var p = range.commonAncestorContainer;
22528 while (p.nodeType == 3) { // text node
22539 * Range intersection.. the hard stuff...
22543 * [ -- selected range --- ]
22547 * if end is before start or hits it. fail.
22548 * if start is after end or hits it fail.
22550 * if either hits (but other is outside. - then it's not
22556 // @see http://www.thismuchiknow.co.uk/?p=64.
22557 rangeIntersectsNode : function(range, node)
22559 var nodeRange = node.ownerDocument.createRange();
22561 nodeRange.selectNode(node);
22563 nodeRange.selectNodeContents(node);
22566 var rangeStartRange = range.cloneRange();
22567 rangeStartRange.collapse(true);
22569 var rangeEndRange = range.cloneRange();
22570 rangeEndRange.collapse(false);
22572 var nodeStartRange = nodeRange.cloneRange();
22573 nodeStartRange.collapse(true);
22575 var nodeEndRange = nodeRange.cloneRange();
22576 nodeEndRange.collapse(false);
22578 return rangeStartRange.compareBoundaryPoints(
22579 Range.START_TO_START, nodeEndRange) == -1 &&
22580 rangeEndRange.compareBoundaryPoints(
22581 Range.START_TO_START, nodeStartRange) == 1;
22585 rangeCompareNode : function(range, node)
22587 var nodeRange = node.ownerDocument.createRange();
22589 nodeRange.selectNode(node);
22591 nodeRange.selectNodeContents(node);
22595 range.collapse(true);
22597 nodeRange.collapse(true);
22599 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22600 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22602 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22604 var nodeIsBefore = ss == 1;
22605 var nodeIsAfter = ee == -1;
22607 if (nodeIsBefore && nodeIsAfter) {
22610 if (!nodeIsBefore && nodeIsAfter) {
22611 return 1; //right trailed.
22614 if (nodeIsBefore && !nodeIsAfter) {
22615 return 2; // left trailed.
22621 // private? - in a new class?
22622 cleanUpPaste : function()
22624 // cleans up the whole document..
22625 Roo.log('cleanuppaste');
22627 this.cleanUpChildren(this.doc.body);
22628 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22629 if (clean != this.doc.body.innerHTML) {
22630 this.doc.body.innerHTML = clean;
22635 cleanWordChars : function(input) {// change the chars to hex code
22636 var he = Roo.HtmlEditorCore;
22638 var output = input;
22639 Roo.each(he.swapCodes, function(sw) {
22640 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22642 output = output.replace(swapper, sw[1]);
22649 cleanUpChildren : function (n)
22651 if (!n.childNodes.length) {
22654 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22655 this.cleanUpChild(n.childNodes[i]);
22662 cleanUpChild : function (node)
22665 //console.log(node);
22666 if (node.nodeName == "#text") {
22667 // clean up silly Windows -- stuff?
22670 if (node.nodeName == "#comment") {
22671 node.parentNode.removeChild(node);
22672 // clean up silly Windows -- stuff?
22675 var lcname = node.tagName.toLowerCase();
22676 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22677 // whitelist of tags..
22679 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22681 node.parentNode.removeChild(node);
22686 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22688 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22689 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22691 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22692 // remove_keep_children = true;
22695 if (remove_keep_children) {
22696 this.cleanUpChildren(node);
22697 // inserts everything just before this node...
22698 while (node.childNodes.length) {
22699 var cn = node.childNodes[0];
22700 node.removeChild(cn);
22701 node.parentNode.insertBefore(cn, node);
22703 node.parentNode.removeChild(node);
22707 if (!node.attributes || !node.attributes.length) {
22708 this.cleanUpChildren(node);
22712 function cleanAttr(n,v)
22715 if (v.match(/^\./) || v.match(/^\//)) {
22718 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22721 if (v.match(/^#/)) {
22724 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22725 node.removeAttribute(n);
22729 var cwhite = this.cwhite;
22730 var cblack = this.cblack;
22732 function cleanStyle(n,v)
22734 if (v.match(/expression/)) { //XSS?? should we even bother..
22735 node.removeAttribute(n);
22739 var parts = v.split(/;/);
22742 Roo.each(parts, function(p) {
22743 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22747 var l = p.split(':').shift().replace(/\s+/g,'');
22748 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22750 if ( cwhite.length && cblack.indexOf(l) > -1) {
22751 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22752 //node.removeAttribute(n);
22756 // only allow 'c whitelisted system attributes'
22757 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22758 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22759 //node.removeAttribute(n);
22769 if (clean.length) {
22770 node.setAttribute(n, clean.join(';'));
22772 node.removeAttribute(n);
22778 for (var i = node.attributes.length-1; i > -1 ; i--) {
22779 var a = node.attributes[i];
22782 if (a.name.toLowerCase().substr(0,2)=='on') {
22783 node.removeAttribute(a.name);
22786 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22787 node.removeAttribute(a.name);
22790 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22791 cleanAttr(a.name,a.value); // fixme..
22794 if (a.name == 'style') {
22795 cleanStyle(a.name,a.value);
22798 /// clean up MS crap..
22799 // tecnically this should be a list of valid class'es..
22802 if (a.name == 'class') {
22803 if (a.value.match(/^Mso/)) {
22804 node.className = '';
22807 if (a.value.match(/^body$/)) {
22808 node.className = '';
22819 this.cleanUpChildren(node);
22825 * Clean up MS wordisms...
22827 cleanWord : function(node)
22832 this.cleanWord(this.doc.body);
22835 if (node.nodeName == "#text") {
22836 // clean up silly Windows -- stuff?
22839 if (node.nodeName == "#comment") {
22840 node.parentNode.removeChild(node);
22841 // clean up silly Windows -- stuff?
22845 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22846 node.parentNode.removeChild(node);
22850 // remove - but keep children..
22851 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22852 while (node.childNodes.length) {
22853 var cn = node.childNodes[0];
22854 node.removeChild(cn);
22855 node.parentNode.insertBefore(cn, node);
22857 node.parentNode.removeChild(node);
22858 this.iterateChildren(node, this.cleanWord);
22862 if (node.className.length) {
22864 var cn = node.className.split(/\W+/);
22866 Roo.each(cn, function(cls) {
22867 if (cls.match(/Mso[a-zA-Z]+/)) {
22872 node.className = cna.length ? cna.join(' ') : '';
22874 node.removeAttribute("class");
22878 if (node.hasAttribute("lang")) {
22879 node.removeAttribute("lang");
22882 if (node.hasAttribute("style")) {
22884 var styles = node.getAttribute("style").split(";");
22886 Roo.each(styles, function(s) {
22887 if (!s.match(/:/)) {
22890 var kv = s.split(":");
22891 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22894 // what ever is left... we allow.
22897 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22898 if (!nstyle.length) {
22899 node.removeAttribute('style');
22902 this.iterateChildren(node, this.cleanWord);
22908 * iterateChildren of a Node, calling fn each time, using this as the scole..
22909 * @param {DomNode} node node to iterate children of.
22910 * @param {Function} fn method of this class to call on each item.
22912 iterateChildren : function(node, fn)
22914 if (!node.childNodes.length) {
22917 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22918 fn.call(this, node.childNodes[i])
22924 * cleanTableWidths.
22926 * Quite often pasting from word etc.. results in tables with column and widths.
22927 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22930 cleanTableWidths : function(node)
22935 this.cleanTableWidths(this.doc.body);
22940 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22943 Roo.log(node.tagName);
22944 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22945 this.iterateChildren(node, this.cleanTableWidths);
22948 if (node.hasAttribute('width')) {
22949 node.removeAttribute('width');
22953 if (node.hasAttribute("style")) {
22956 var styles = node.getAttribute("style").split(";");
22958 Roo.each(styles, function(s) {
22959 if (!s.match(/:/)) {
22962 var kv = s.split(":");
22963 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22966 // what ever is left... we allow.
22969 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22970 if (!nstyle.length) {
22971 node.removeAttribute('style');
22975 this.iterateChildren(node, this.cleanTableWidths);
22983 domToHTML : function(currentElement, depth, nopadtext) {
22985 depth = depth || 0;
22986 nopadtext = nopadtext || false;
22988 if (!currentElement) {
22989 return this.domToHTML(this.doc.body);
22992 //Roo.log(currentElement);
22994 var allText = false;
22995 var nodeName = currentElement.nodeName;
22996 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22998 if (nodeName == '#text') {
23000 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23005 if (nodeName != 'BODY') {
23008 // Prints the node tagName, such as <A>, <IMG>, etc
23011 for(i = 0; i < currentElement.attributes.length;i++) {
23013 var aname = currentElement.attributes.item(i).name;
23014 if (!currentElement.attributes.item(i).value.length) {
23017 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23020 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23029 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23032 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23037 // Traverse the tree
23039 var currentElementChild = currentElement.childNodes.item(i);
23040 var allText = true;
23041 var innerHTML = '';
23043 while (currentElementChild) {
23044 // Formatting code (indent the tree so it looks nice on the screen)
23045 var nopad = nopadtext;
23046 if (lastnode == 'SPAN') {
23050 if (currentElementChild.nodeName == '#text') {
23051 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23052 toadd = nopadtext ? toadd : toadd.trim();
23053 if (!nopad && toadd.length > 80) {
23054 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23056 innerHTML += toadd;
23059 currentElementChild = currentElement.childNodes.item(i);
23065 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23067 // Recursively traverse the tree structure of the child node
23068 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23069 lastnode = currentElementChild.nodeName;
23071 currentElementChild=currentElement.childNodes.item(i);
23077 // The remaining code is mostly for formatting the tree
23078 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23083 ret+= "</"+tagName+">";
23089 applyBlacklists : function()
23091 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23092 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23096 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23097 if (b.indexOf(tag) > -1) {
23100 this.white.push(tag);
23104 Roo.each(w, function(tag) {
23105 if (b.indexOf(tag) > -1) {
23108 if (this.white.indexOf(tag) > -1) {
23111 this.white.push(tag);
23116 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23117 if (w.indexOf(tag) > -1) {
23120 this.black.push(tag);
23124 Roo.each(b, function(tag) {
23125 if (w.indexOf(tag) > -1) {
23128 if (this.black.indexOf(tag) > -1) {
23131 this.black.push(tag);
23136 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23137 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23141 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23142 if (b.indexOf(tag) > -1) {
23145 this.cwhite.push(tag);
23149 Roo.each(w, function(tag) {
23150 if (b.indexOf(tag) > -1) {
23153 if (this.cwhite.indexOf(tag) > -1) {
23156 this.cwhite.push(tag);
23161 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23162 if (w.indexOf(tag) > -1) {
23165 this.cblack.push(tag);
23169 Roo.each(b, function(tag) {
23170 if (w.indexOf(tag) > -1) {
23173 if (this.cblack.indexOf(tag) > -1) {
23176 this.cblack.push(tag);
23181 setStylesheets : function(stylesheets)
23183 if(typeof(stylesheets) == 'string'){
23184 Roo.get(this.iframe.contentDocument.head).createChild({
23186 rel : 'stylesheet',
23195 Roo.each(stylesheets, function(s) {
23200 Roo.get(_this.iframe.contentDocument.head).createChild({
23202 rel : 'stylesheet',
23211 removeStylesheets : function()
23215 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23220 setStyle : function(style)
23222 Roo.get(this.iframe.contentDocument.head).createChild({
23231 // hide stuff that is not compatible
23245 * @event specialkey
23249 * @cfg {String} fieldClass @hide
23252 * @cfg {String} focusClass @hide
23255 * @cfg {String} autoCreate @hide
23258 * @cfg {String} inputType @hide
23261 * @cfg {String} invalidClass @hide
23264 * @cfg {String} invalidText @hide
23267 * @cfg {String} msgFx @hide
23270 * @cfg {String} validateOnBlur @hide
23274 Roo.HtmlEditorCore.white = [
23275 'area', 'br', 'img', 'input', 'hr', 'wbr',
23277 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23278 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23279 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23280 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23281 'table', 'ul', 'xmp',
23283 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23286 'dir', 'menu', 'ol', 'ul', 'dl',
23292 Roo.HtmlEditorCore.black = [
23293 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23295 'base', 'basefont', 'bgsound', 'blink', 'body',
23296 'frame', 'frameset', 'head', 'html', 'ilayer',
23297 'iframe', 'layer', 'link', 'meta', 'object',
23298 'script', 'style' ,'title', 'xml' // clean later..
23300 Roo.HtmlEditorCore.clean = [
23301 'script', 'style', 'title', 'xml'
23303 Roo.HtmlEditorCore.remove = [
23308 Roo.HtmlEditorCore.ablack = [
23312 Roo.HtmlEditorCore.aclean = [
23313 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23317 Roo.HtmlEditorCore.pwhite= [
23318 'http', 'https', 'mailto'
23321 // white listed style attributes.
23322 Roo.HtmlEditorCore.cwhite= [
23323 // 'text-align', /// default is to allow most things..
23329 // black listed style attributes.
23330 Roo.HtmlEditorCore.cblack= [
23331 // 'font-size' -- this can be set by the project
23335 Roo.HtmlEditorCore.swapCodes =[
23354 * @class Roo.bootstrap.HtmlEditor
23355 * @extends Roo.bootstrap.TextArea
23356 * Bootstrap HtmlEditor class
23359 * Create a new HtmlEditor
23360 * @param {Object} config The config object
23363 Roo.bootstrap.HtmlEditor = function(config){
23364 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23365 if (!this.toolbars) {
23366 this.toolbars = [];
23369 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23372 * @event initialize
23373 * Fires when the editor is fully initialized (including the iframe)
23374 * @param {HtmlEditor} this
23379 * Fires when the editor is first receives the focus. Any insertion must wait
23380 * until after this event.
23381 * @param {HtmlEditor} this
23385 * @event beforesync
23386 * Fires before the textarea is updated with content from the editor iframe. Return false
23387 * to cancel the sync.
23388 * @param {HtmlEditor} this
23389 * @param {String} html
23393 * @event beforepush
23394 * Fires before the iframe editor is updated with content from the textarea. Return false
23395 * to cancel the push.
23396 * @param {HtmlEditor} this
23397 * @param {String} html
23402 * Fires when the textarea is updated with content from the editor iframe.
23403 * @param {HtmlEditor} this
23404 * @param {String} html
23409 * Fires when the iframe editor is updated with content from the textarea.
23410 * @param {HtmlEditor} this
23411 * @param {String} html
23415 * @event editmodechange
23416 * Fires when the editor switches edit modes
23417 * @param {HtmlEditor} this
23418 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23420 editmodechange: true,
23422 * @event editorevent
23423 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23424 * @param {HtmlEditor} this
23428 * @event firstfocus
23429 * Fires when on first focus - needed by toolbars..
23430 * @param {HtmlEditor} this
23435 * Auto save the htmlEditor value as a file into Events
23436 * @param {HtmlEditor} this
23440 * @event savedpreview
23441 * preview the saved version of htmlEditor
23442 * @param {HtmlEditor} this
23449 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23453 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23458 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23463 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23468 * @cfg {Number} height (in pixels)
23472 * @cfg {Number} width (in pixels)
23477 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23480 stylesheets: false,
23485 // private properties
23486 validationEvent : false,
23488 initialized : false,
23491 onFocus : Roo.emptyFn,
23493 hideMode:'offsets',
23495 tbContainer : false,
23499 toolbarContainer :function() {
23500 return this.wrap.select('.x-html-editor-tb',true).first();
23504 * Protected method that will not generally be called directly. It
23505 * is called when the editor creates its toolbar. Override this method if you need to
23506 * add custom toolbar buttons.
23507 * @param {HtmlEditor} editor
23509 createToolbar : function(){
23510 Roo.log('renewing');
23511 Roo.log("create toolbars");
23513 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23514 this.toolbars[0].render(this.toolbarContainer());
23518 // if (!editor.toolbars || !editor.toolbars.length) {
23519 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23522 // for (var i =0 ; i < editor.toolbars.length;i++) {
23523 // editor.toolbars[i] = Roo.factory(
23524 // typeof(editor.toolbars[i]) == 'string' ?
23525 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23526 // Roo.bootstrap.HtmlEditor);
23527 // editor.toolbars[i].init(editor);
23533 onRender : function(ct, position)
23535 // Roo.log("Call onRender: " + this.xtype);
23537 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23539 this.wrap = this.inputEl().wrap({
23540 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23543 this.editorcore.onRender(ct, position);
23545 if (this.resizable) {
23546 this.resizeEl = new Roo.Resizable(this.wrap, {
23550 minHeight : this.height,
23551 height: this.height,
23552 handles : this.resizable,
23555 resize : function(r, w, h) {
23556 _t.onResize(w,h); // -something
23562 this.createToolbar(this);
23565 if(!this.width && this.resizable){
23566 this.setSize(this.wrap.getSize());
23568 if (this.resizeEl) {
23569 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23570 // should trigger onReize..
23576 onResize : function(w, h)
23578 Roo.log('resize: ' +w + ',' + h );
23579 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23583 if(this.inputEl() ){
23584 if(typeof w == 'number'){
23585 var aw = w - this.wrap.getFrameWidth('lr');
23586 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23589 if(typeof h == 'number'){
23590 var tbh = -11; // fixme it needs to tool bar size!
23591 for (var i =0; i < this.toolbars.length;i++) {
23592 // fixme - ask toolbars for heights?
23593 tbh += this.toolbars[i].el.getHeight();
23594 //if (this.toolbars[i].footer) {
23595 // tbh += this.toolbars[i].footer.el.getHeight();
23603 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23604 ah -= 5; // knock a few pixes off for look..
23605 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23609 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23610 this.editorcore.onResize(ew,eh);
23615 * Toggles the editor between standard and source edit mode.
23616 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23618 toggleSourceEdit : function(sourceEditMode)
23620 this.editorcore.toggleSourceEdit(sourceEditMode);
23622 if(this.editorcore.sourceEditMode){
23623 Roo.log('editor - showing textarea');
23626 // Roo.log(this.syncValue());
23628 this.inputEl().removeClass(['hide', 'x-hidden']);
23629 this.inputEl().dom.removeAttribute('tabIndex');
23630 this.inputEl().focus();
23632 Roo.log('editor - hiding textarea');
23634 // Roo.log(this.pushValue());
23637 this.inputEl().addClass(['hide', 'x-hidden']);
23638 this.inputEl().dom.setAttribute('tabIndex', -1);
23639 //this.deferFocus();
23642 if(this.resizable){
23643 this.setSize(this.wrap.getSize());
23646 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23649 // private (for BoxComponent)
23650 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23652 // private (for BoxComponent)
23653 getResizeEl : function(){
23657 // private (for BoxComponent)
23658 getPositionEl : function(){
23663 initEvents : function(){
23664 this.originalValue = this.getValue();
23668 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23671 // markInvalid : Roo.emptyFn,
23673 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23676 // clearInvalid : Roo.emptyFn,
23678 setValue : function(v){
23679 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23680 this.editorcore.pushValue();
23685 deferFocus : function(){
23686 this.focus.defer(10, this);
23690 focus : function(){
23691 this.editorcore.focus();
23697 onDestroy : function(){
23703 for (var i =0; i < this.toolbars.length;i++) {
23704 // fixme - ask toolbars for heights?
23705 this.toolbars[i].onDestroy();
23708 this.wrap.dom.innerHTML = '';
23709 this.wrap.remove();
23714 onFirstFocus : function(){
23715 //Roo.log("onFirstFocus");
23716 this.editorcore.onFirstFocus();
23717 for (var i =0; i < this.toolbars.length;i++) {
23718 this.toolbars[i].onFirstFocus();
23724 syncValue : function()
23726 this.editorcore.syncValue();
23729 pushValue : function()
23731 this.editorcore.pushValue();
23735 // hide stuff that is not compatible
23749 * @event specialkey
23753 * @cfg {String} fieldClass @hide
23756 * @cfg {String} focusClass @hide
23759 * @cfg {String} autoCreate @hide
23762 * @cfg {String} inputType @hide
23765 * @cfg {String} invalidClass @hide
23768 * @cfg {String} invalidText @hide
23771 * @cfg {String} msgFx @hide
23774 * @cfg {String} validateOnBlur @hide
23783 Roo.namespace('Roo.bootstrap.htmleditor');
23785 * @class Roo.bootstrap.HtmlEditorToolbar1
23790 new Roo.bootstrap.HtmlEditor({
23793 new Roo.bootstrap.HtmlEditorToolbar1({
23794 disable : { fonts: 1 , format: 1, ..., ... , ...],
23800 * @cfg {Object} disable List of elements to disable..
23801 * @cfg {Array} btns List of additional buttons.
23805 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23808 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23811 Roo.apply(this, config);
23813 // default disabled, based on 'good practice'..
23814 this.disable = this.disable || {};
23815 Roo.applyIf(this.disable, {
23818 specialElements : true
23820 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23822 this.editor = config.editor;
23823 this.editorcore = config.editor.editorcore;
23825 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23827 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23828 // dont call parent... till later.
23830 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23835 editorcore : false,
23840 "h1","h2","h3","h4","h5","h6",
23842 "abbr", "acronym", "address", "cite", "samp", "var",
23846 onRender : function(ct, position)
23848 // Roo.log("Call onRender: " + this.xtype);
23850 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23852 this.el.dom.style.marginBottom = '0';
23854 var editorcore = this.editorcore;
23855 var editor= this.editor;
23858 var btn = function(id,cmd , toggle, handler, html){
23860 var event = toggle ? 'toggle' : 'click';
23865 xns: Roo.bootstrap,
23868 enableToggle:toggle !== false,
23870 pressed : toggle ? false : null,
23873 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23874 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23880 // var cb_box = function...
23885 xns: Roo.bootstrap,
23886 glyphicon : 'font',
23890 xns: Roo.bootstrap,
23894 Roo.each(this.formats, function(f) {
23895 style.menu.items.push({
23897 xns: Roo.bootstrap,
23898 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23903 editorcore.insertTag(this.tagname);
23910 children.push(style);
23912 btn('bold',false,true);
23913 btn('italic',false,true);
23914 btn('align-left', 'justifyleft',true);
23915 btn('align-center', 'justifycenter',true);
23916 btn('align-right' , 'justifyright',true);
23917 btn('link', false, false, function(btn) {
23918 //Roo.log("create link?");
23919 var url = prompt(this.createLinkText, this.defaultLinkValue);
23920 if(url && url != 'http:/'+'/'){
23921 this.editorcore.relayCmd('createlink', url);
23924 btn('list','insertunorderedlist',true);
23925 btn('pencil', false,true, function(btn){
23927 this.toggleSourceEdit(btn.pressed);
23930 if (this.editor.btns.length > 0) {
23931 for (var i = 0; i<this.editor.btns.length; i++) {
23932 children.push(this.editor.btns[i]);
23940 xns: Roo.bootstrap,
23945 xns: Roo.bootstrap,
23950 cog.menu.items.push({
23952 xns: Roo.bootstrap,
23953 html : Clean styles,
23958 editorcore.insertTag(this.tagname);
23967 this.xtype = 'NavSimplebar';
23969 for(var i=0;i< children.length;i++) {
23971 this.buttons.add(this.addxtypeChild(children[i]));
23975 editor.on('editorevent', this.updateToolbar, this);
23977 onBtnClick : function(id)
23979 this.editorcore.relayCmd(id);
23980 this.editorcore.focus();
23984 * Protected method that will not generally be called directly. It triggers
23985 * a toolbar update by reading the markup state of the current selection in the editor.
23987 updateToolbar: function(){
23989 if(!this.editorcore.activated){
23990 this.editor.onFirstFocus(); // is this neeed?
23994 var btns = this.buttons;
23995 var doc = this.editorcore.doc;
23996 btns.get('bold').setActive(doc.queryCommandState('bold'));
23997 btns.get('italic').setActive(doc.queryCommandState('italic'));
23998 //btns.get('underline').setActive(doc.queryCommandState('underline'));
24000 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24001 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24002 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24004 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24005 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24008 var ans = this.editorcore.getAllAncestors();
24009 if (this.formatCombo) {
24012 var store = this.formatCombo.store;
24013 this.formatCombo.setValue("");
24014 for (var i =0; i < ans.length;i++) {
24015 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24017 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24025 // hides menus... - so this cant be on a menu...
24026 Roo.bootstrap.MenuMgr.hideAll();
24028 Roo.bootstrap.MenuMgr.hideAll();
24029 //this.editorsyncValue();
24031 onFirstFocus: function() {
24032 this.buttons.each(function(item){
24036 toggleSourceEdit : function(sourceEditMode){
24039 if(sourceEditMode){
24040 Roo.log("disabling buttons");
24041 this.buttons.each( function(item){
24042 if(item.cmd != 'pencil'){
24048 Roo.log("enabling buttons");
24049 if(this.editorcore.initialized){
24050 this.buttons.each( function(item){
24056 Roo.log("calling toggole on editor");
24057 // tell the editor that it's been pressed..
24058 this.editor.toggleSourceEdit(sourceEditMode);
24068 * @class Roo.bootstrap.Table.AbstractSelectionModel
24069 * @extends Roo.util.Observable
24070 * Abstract base class for grid SelectionModels. It provides the interface that should be
24071 * implemented by descendant classes. This class should not be directly instantiated.
24074 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24075 this.locked = false;
24076 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24080 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24081 /** @ignore Called by the grid automatically. Do not call directly. */
24082 init : function(grid){
24088 * Locks the selections.
24091 this.locked = true;
24095 * Unlocks the selections.
24097 unlock : function(){
24098 this.locked = false;
24102 * Returns true if the selections are locked.
24103 * @return {Boolean}
24105 isLocked : function(){
24106 return this.locked;
24110 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24111 * @class Roo.bootstrap.Table.RowSelectionModel
24112 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24113 * It supports multiple selections and keyboard selection/navigation.
24115 * @param {Object} config
24118 Roo.bootstrap.Table.RowSelectionModel = function(config){
24119 Roo.apply(this, config);
24120 this.selections = new Roo.util.MixedCollection(false, function(o){
24125 this.lastActive = false;
24129 * @event selectionchange
24130 * Fires when the selection changes
24131 * @param {SelectionModel} this
24133 "selectionchange" : true,
24135 * @event afterselectionchange
24136 * Fires after the selection changes (eg. by key press or clicking)
24137 * @param {SelectionModel} this
24139 "afterselectionchange" : true,
24141 * @event beforerowselect
24142 * Fires when a row is selected being selected, return false to cancel.
24143 * @param {SelectionModel} this
24144 * @param {Number} rowIndex The selected index
24145 * @param {Boolean} keepExisting False if other selections will be cleared
24147 "beforerowselect" : true,
24150 * Fires when a row is selected.
24151 * @param {SelectionModel} this
24152 * @param {Number} rowIndex The selected index
24153 * @param {Roo.data.Record} r The record
24155 "rowselect" : true,
24157 * @event rowdeselect
24158 * Fires when a row is deselected.
24159 * @param {SelectionModel} this
24160 * @param {Number} rowIndex The selected index
24162 "rowdeselect" : true
24164 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24165 this.locked = false;
24168 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24170 * @cfg {Boolean} singleSelect
24171 * True to allow selection of only one row at a time (defaults to false)
24173 singleSelect : false,
24176 initEvents : function()
24179 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24180 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24181 //}else{ // allow click to work like normal
24182 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24184 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24185 this.grid.on("rowclick", this.handleMouseDown, this);
24187 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24188 "up" : function(e){
24190 this.selectPrevious(e.shiftKey);
24191 }else if(this.last !== false && this.lastActive !== false){
24192 var last = this.last;
24193 this.selectRange(this.last, this.lastActive-1);
24194 this.grid.getView().focusRow(this.lastActive);
24195 if(last !== false){
24199 this.selectFirstRow();
24201 this.fireEvent("afterselectionchange", this);
24203 "down" : function(e){
24205 this.selectNext(e.shiftKey);
24206 }else if(this.last !== false && this.lastActive !== false){
24207 var last = this.last;
24208 this.selectRange(this.last, this.lastActive+1);
24209 this.grid.getView().focusRow(this.lastActive);
24210 if(last !== false){
24214 this.selectFirstRow();
24216 this.fireEvent("afterselectionchange", this);
24220 this.grid.store.on('load', function(){
24221 this.selections.clear();
24224 var view = this.grid.view;
24225 view.on("refresh", this.onRefresh, this);
24226 view.on("rowupdated", this.onRowUpdated, this);
24227 view.on("rowremoved", this.onRemove, this);
24232 onRefresh : function()
24234 var ds = this.grid.store, i, v = this.grid.view;
24235 var s = this.selections;
24236 s.each(function(r){
24237 if((i = ds.indexOfId(r.id)) != -1){
24246 onRemove : function(v, index, r){
24247 this.selections.remove(r);
24251 onRowUpdated : function(v, index, r){
24252 if(this.isSelected(r)){
24253 v.onRowSelect(index);
24259 * @param {Array} records The records to select
24260 * @param {Boolean} keepExisting (optional) True to keep existing selections
24262 selectRecords : function(records, keepExisting)
24265 this.clearSelections();
24267 var ds = this.grid.store;
24268 for(var i = 0, len = records.length; i < len; i++){
24269 this.selectRow(ds.indexOf(records[i]), true);
24274 * Gets the number of selected rows.
24277 getCount : function(){
24278 return this.selections.length;
24282 * Selects the first row in the grid.
24284 selectFirstRow : function(){
24289 * Select the last row.
24290 * @param {Boolean} keepExisting (optional) True to keep existing selections
24292 selectLastRow : function(keepExisting){
24293 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24294 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24298 * Selects the row immediately following the last selected row.
24299 * @param {Boolean} keepExisting (optional) True to keep existing selections
24301 selectNext : function(keepExisting)
24303 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24304 this.selectRow(this.last+1, keepExisting);
24305 this.grid.getView().focusRow(this.last);
24310 * Selects the row that precedes the last selected row.
24311 * @param {Boolean} keepExisting (optional) True to keep existing selections
24313 selectPrevious : function(keepExisting){
24315 this.selectRow(this.last-1, keepExisting);
24316 this.grid.getView().focusRow(this.last);
24321 * Returns the selected records
24322 * @return {Array} Array of selected records
24324 getSelections : function(){
24325 return [].concat(this.selections.items);
24329 * Returns the first selected record.
24332 getSelected : function(){
24333 return this.selections.itemAt(0);
24338 * Clears all selections.
24340 clearSelections : function(fast)
24346 var ds = this.grid.store;
24347 var s = this.selections;
24348 s.each(function(r){
24349 this.deselectRow(ds.indexOfId(r.id));
24353 this.selections.clear();
24360 * Selects all rows.
24362 selectAll : function(){
24366 this.selections.clear();
24367 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24368 this.selectRow(i, true);
24373 * Returns True if there is a selection.
24374 * @return {Boolean}
24376 hasSelection : function(){
24377 return this.selections.length > 0;
24381 * Returns True if the specified row is selected.
24382 * @param {Number/Record} record The record or index of the record to check
24383 * @return {Boolean}
24385 isSelected : function(index){
24386 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24387 return (r && this.selections.key(r.id) ? true : false);
24391 * Returns True if the specified record id is selected.
24392 * @param {String} id The id of record to check
24393 * @return {Boolean}
24395 isIdSelected : function(id){
24396 return (this.selections.key(id) ? true : false);
24401 handleMouseDBClick : function(e, t){
24405 handleMouseDown : function(e, t)
24407 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24408 if(this.isLocked() || rowIndex < 0 ){
24411 if(e.shiftKey && this.last !== false){
24412 var last = this.last;
24413 this.selectRange(last, rowIndex, e.ctrlKey);
24414 this.last = last; // reset the last
24418 var isSelected = this.isSelected(rowIndex);
24419 //Roo.log("select row:" + rowIndex);
24421 this.deselectRow(rowIndex);
24423 this.selectRow(rowIndex, true);
24427 if(e.button !== 0 && isSelected){
24428 alert('rowIndex 2: ' + rowIndex);
24429 view.focusRow(rowIndex);
24430 }else if(e.ctrlKey && isSelected){
24431 this.deselectRow(rowIndex);
24432 }else if(!isSelected){
24433 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24434 view.focusRow(rowIndex);
24438 this.fireEvent("afterselectionchange", this);
24441 handleDragableRowClick : function(grid, rowIndex, e)
24443 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24444 this.selectRow(rowIndex, false);
24445 grid.view.focusRow(rowIndex);
24446 this.fireEvent("afterselectionchange", this);
24451 * Selects multiple rows.
24452 * @param {Array} rows Array of the indexes of the row to select
24453 * @param {Boolean} keepExisting (optional) True to keep existing selections
24455 selectRows : function(rows, keepExisting){
24457 this.clearSelections();
24459 for(var i = 0, len = rows.length; i < len; i++){
24460 this.selectRow(rows[i], true);
24465 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24466 * @param {Number} startRow The index of the first row in the range
24467 * @param {Number} endRow The index of the last row in the range
24468 * @param {Boolean} keepExisting (optional) True to retain existing selections
24470 selectRange : function(startRow, endRow, keepExisting){
24475 this.clearSelections();
24477 if(startRow <= endRow){
24478 for(var i = startRow; i <= endRow; i++){
24479 this.selectRow(i, true);
24482 for(var i = startRow; i >= endRow; i--){
24483 this.selectRow(i, true);
24489 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24490 * @param {Number} startRow The index of the first row in the range
24491 * @param {Number} endRow The index of the last row in the range
24493 deselectRange : function(startRow, endRow, preventViewNotify){
24497 for(var i = startRow; i <= endRow; i++){
24498 this.deselectRow(i, preventViewNotify);
24504 * @param {Number} row The index of the row to select
24505 * @param {Boolean} keepExisting (optional) True to keep existing selections
24507 selectRow : function(index, keepExisting, preventViewNotify)
24509 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24512 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24513 if(!keepExisting || this.singleSelect){
24514 this.clearSelections();
24517 var r = this.grid.store.getAt(index);
24518 //console.log('selectRow - record id :' + r.id);
24520 this.selections.add(r);
24521 this.last = this.lastActive = index;
24522 if(!preventViewNotify){
24523 var proxy = new Roo.Element(
24524 this.grid.getRowDom(index)
24526 proxy.addClass('bg-info info');
24528 this.fireEvent("rowselect", this, index, r);
24529 this.fireEvent("selectionchange", this);
24535 * @param {Number} row The index of the row to deselect
24537 deselectRow : function(index, preventViewNotify)
24542 if(this.last == index){
24545 if(this.lastActive == index){
24546 this.lastActive = false;
24549 var r = this.grid.store.getAt(index);
24554 this.selections.remove(r);
24555 //.console.log('deselectRow - record id :' + r.id);
24556 if(!preventViewNotify){
24558 var proxy = new Roo.Element(
24559 this.grid.getRowDom(index)
24561 proxy.removeClass('bg-info info');
24563 this.fireEvent("rowdeselect", this, index);
24564 this.fireEvent("selectionchange", this);
24568 restoreLast : function(){
24570 this.last = this._last;
24575 acceptsNav : function(row, col, cm){
24576 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24580 onEditorKey : function(field, e){
24581 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24586 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24588 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24590 }else if(k == e.ENTER && !e.ctrlKey){
24594 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24596 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24598 }else if(k == e.ESC){
24602 g.startEditing(newCell[0], newCell[1]);
24608 * Ext JS Library 1.1.1
24609 * Copyright(c) 2006-2007, Ext JS, LLC.
24611 * Originally Released Under LGPL - original licence link has changed is not relivant.
24614 * <script type="text/javascript">
24618 * @class Roo.bootstrap.PagingToolbar
24619 * @extends Roo.bootstrap.NavSimplebar
24620 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24622 * Create a new PagingToolbar
24623 * @param {Object} config The config object
24624 * @param {Roo.data.Store} store
24626 Roo.bootstrap.PagingToolbar = function(config)
24628 // old args format still supported... - xtype is prefered..
24629 // created from xtype...
24631 this.ds = config.dataSource;
24633 if (config.store && !this.ds) {
24634 this.store= Roo.factory(config.store, Roo.data);
24635 this.ds = this.store;
24636 this.ds.xmodule = this.xmodule || false;
24639 this.toolbarItems = [];
24640 if (config.items) {
24641 this.toolbarItems = config.items;
24644 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24649 this.bind(this.ds);
24652 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24656 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24658 * @cfg {Roo.data.Store} dataSource
24659 * The underlying data store providing the paged data
24662 * @cfg {String/HTMLElement/Element} container
24663 * container The id or element that will contain the toolbar
24666 * @cfg {Boolean} displayInfo
24667 * True to display the displayMsg (defaults to false)
24670 * @cfg {Number} pageSize
24671 * The number of records to display per page (defaults to 20)
24675 * @cfg {String} displayMsg
24676 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24678 displayMsg : 'Displaying {0} - {1} of {2}',
24680 * @cfg {String} emptyMsg
24681 * The message to display when no records are found (defaults to "No data to display")
24683 emptyMsg : 'No data to display',
24685 * Customizable piece of the default paging text (defaults to "Page")
24688 beforePageText : "Page",
24690 * Customizable piece of the default paging text (defaults to "of %0")
24693 afterPageText : "of {0}",
24695 * Customizable piece of the default paging text (defaults to "First Page")
24698 firstText : "First Page",
24700 * Customizable piece of the default paging text (defaults to "Previous Page")
24703 prevText : "Previous Page",
24705 * Customizable piece of the default paging text (defaults to "Next Page")
24708 nextText : "Next Page",
24710 * Customizable piece of the default paging text (defaults to "Last Page")
24713 lastText : "Last Page",
24715 * Customizable piece of the default paging text (defaults to "Refresh")
24718 refreshText : "Refresh",
24722 onRender : function(ct, position)
24724 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24725 this.navgroup.parentId = this.id;
24726 this.navgroup.onRender(this.el, null);
24727 // add the buttons to the navgroup
24729 if(this.displayInfo){
24730 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24731 this.displayEl = this.el.select('.x-paging-info', true).first();
24732 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24733 // this.displayEl = navel.el.select('span',true).first();
24739 Roo.each(_this.buttons, function(e){ // this might need to use render????
24740 Roo.factory(e).render(_this.el);
24744 Roo.each(_this.toolbarItems, function(e) {
24745 _this.navgroup.addItem(e);
24749 this.first = this.navgroup.addItem({
24750 tooltip: this.firstText,
24752 icon : 'fa fa-step-backward',
24754 preventDefault: true,
24755 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24758 this.prev = this.navgroup.addItem({
24759 tooltip: this.prevText,
24761 icon : 'fa fa-backward',
24763 preventDefault: true,
24764 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24766 //this.addSeparator();
24769 var field = this.navgroup.addItem( {
24771 cls : 'x-paging-position',
24773 html : this.beforePageText +
24774 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24775 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24778 this.field = field.el.select('input', true).first();
24779 this.field.on("keydown", this.onPagingKeydown, this);
24780 this.field.on("focus", function(){this.dom.select();});
24783 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24784 //this.field.setHeight(18);
24785 //this.addSeparator();
24786 this.next = this.navgroup.addItem({
24787 tooltip: this.nextText,
24789 html : ' <i class="fa fa-forward">',
24791 preventDefault: true,
24792 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24794 this.last = this.navgroup.addItem({
24795 tooltip: this.lastText,
24796 icon : 'fa fa-step-forward',
24799 preventDefault: true,
24800 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24802 //this.addSeparator();
24803 this.loading = this.navgroup.addItem({
24804 tooltip: this.refreshText,
24805 icon: 'fa fa-refresh',
24806 preventDefault: true,
24807 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24813 updateInfo : function(){
24814 if(this.displayEl){
24815 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24816 var msg = count == 0 ?
24820 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24822 this.displayEl.update(msg);
24827 onLoad : function(ds, r, o)
24829 this.cursor = o.params.start ? o.params.start : 0;
24831 var d = this.getPageData(),
24836 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24837 this.field.dom.value = ap;
24838 this.first.setDisabled(ap == 1);
24839 this.prev.setDisabled(ap == 1);
24840 this.next.setDisabled(ap == ps);
24841 this.last.setDisabled(ap == ps);
24842 this.loading.enable();
24847 getPageData : function(){
24848 var total = this.ds.getTotalCount();
24851 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24852 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24857 onLoadError : function(){
24858 this.loading.enable();
24862 onPagingKeydown : function(e){
24863 var k = e.getKey();
24864 var d = this.getPageData();
24866 var v = this.field.dom.value, pageNum;
24867 if(!v || isNaN(pageNum = parseInt(v, 10))){
24868 this.field.dom.value = d.activePage;
24871 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24872 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24875 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))
24877 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24878 this.field.dom.value = pageNum;
24879 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24882 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24884 var v = this.field.dom.value, pageNum;
24885 var increment = (e.shiftKey) ? 10 : 1;
24886 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24889 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24890 this.field.dom.value = d.activePage;
24893 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24895 this.field.dom.value = parseInt(v, 10) + increment;
24896 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24897 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24904 beforeLoad : function(){
24906 this.loading.disable();
24911 onClick : function(which){
24920 ds.load({params:{start: 0, limit: this.pageSize}});
24923 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24926 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24929 var total = ds.getTotalCount();
24930 var extra = total % this.pageSize;
24931 var lastStart = extra ? (total - extra) : total-this.pageSize;
24932 ds.load({params:{start: lastStart, limit: this.pageSize}});
24935 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24941 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24942 * @param {Roo.data.Store} store The data store to unbind
24944 unbind : function(ds){
24945 ds.un("beforeload", this.beforeLoad, this);
24946 ds.un("load", this.onLoad, this);
24947 ds.un("loadexception", this.onLoadError, this);
24948 ds.un("remove", this.updateInfo, this);
24949 ds.un("add", this.updateInfo, this);
24950 this.ds = undefined;
24954 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24955 * @param {Roo.data.Store} store The data store to bind
24957 bind : function(ds){
24958 ds.on("beforeload", this.beforeLoad, this);
24959 ds.on("load", this.onLoad, this);
24960 ds.on("loadexception", this.onLoadError, this);
24961 ds.on("remove", this.updateInfo, this);
24962 ds.on("add", this.updateInfo, this);
24973 * @class Roo.bootstrap.MessageBar
24974 * @extends Roo.bootstrap.Component
24975 * Bootstrap MessageBar class
24976 * @cfg {String} html contents of the MessageBar
24977 * @cfg {String} weight (info | success | warning | danger) default info
24978 * @cfg {String} beforeClass insert the bar before the given class
24979 * @cfg {Boolean} closable (true | false) default false
24980 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24983 * Create a new Element
24984 * @param {Object} config The config object
24987 Roo.bootstrap.MessageBar = function(config){
24988 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24991 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24997 beforeClass: 'bootstrap-sticky-wrap',
24999 getAutoCreate : function(){
25003 cls: 'alert alert-dismissable alert-' + this.weight,
25008 html: this.html || ''
25014 cfg.cls += ' alert-messages-fixed';
25028 onRender : function(ct, position)
25030 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25033 var cfg = Roo.apply({}, this.getAutoCreate());
25037 cfg.cls += ' ' + this.cls;
25040 cfg.style = this.style;
25042 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25044 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25047 this.el.select('>button.close').on('click', this.hide, this);
25053 if (!this.rendered) {
25059 this.fireEvent('show', this);
25065 if (!this.rendered) {
25071 this.fireEvent('hide', this);
25074 update : function()
25076 // var e = this.el.dom.firstChild;
25078 // if(this.closable){
25079 // e = e.nextSibling;
25082 // e.data = this.html || '';
25084 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25100 * @class Roo.bootstrap.Graph
25101 * @extends Roo.bootstrap.Component
25102 * Bootstrap Graph class
25106 @cfg {String} graphtype bar | vbar | pie
25107 @cfg {number} g_x coodinator | centre x (pie)
25108 @cfg {number} g_y coodinator | centre y (pie)
25109 @cfg {number} g_r radius (pie)
25110 @cfg {number} g_height height of the chart (respected by all elements in the set)
25111 @cfg {number} g_width width of the chart (respected by all elements in the set)
25112 @cfg {Object} title The title of the chart
25115 -opts (object) options for the chart
25117 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25118 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25120 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.
25121 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25123 o stretch (boolean)
25125 -opts (object) options for the pie
25128 o startAngle (number)
25129 o endAngle (number)
25133 * Create a new Input
25134 * @param {Object} config The config object
25137 Roo.bootstrap.Graph = function(config){
25138 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25144 * The img click event for the img.
25145 * @param {Roo.EventObject} e
25151 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25162 //g_colors: this.colors,
25169 getAutoCreate : function(){
25180 onRender : function(ct,position){
25183 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25185 if (typeof(Raphael) == 'undefined') {
25186 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25190 this.raphael = Raphael(this.el.dom);
25192 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25193 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25194 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25195 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25197 r.text(160, 10, "Single Series Chart").attr(txtattr);
25198 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25199 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25200 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25202 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25203 r.barchart(330, 10, 300, 220, data1);
25204 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25205 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25208 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25209 // r.barchart(30, 30, 560, 250, xdata, {
25210 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25211 // axis : "0 0 1 1",
25212 // axisxlabels : xdata
25213 // //yvalues : cols,
25216 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25218 // this.load(null,xdata,{
25219 // axis : "0 0 1 1",
25220 // axisxlabels : xdata
25225 load : function(graphtype,xdata,opts)
25227 this.raphael.clear();
25229 graphtype = this.graphtype;
25234 var r = this.raphael,
25235 fin = function () {
25236 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25238 fout = function () {
25239 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25241 pfin = function() {
25242 this.sector.stop();
25243 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25246 this.label[0].stop();
25247 this.label[0].attr({ r: 7.5 });
25248 this.label[1].attr({ "font-weight": 800 });
25251 pfout = function() {
25252 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25255 this.label[0].animate({ r: 5 }, 500, "bounce");
25256 this.label[1].attr({ "font-weight": 400 });
25262 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25265 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25268 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25269 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25271 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25278 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25283 setTitle: function(o)
25288 initEvents: function() {
25291 this.el.on('click', this.onClick, this);
25295 onClick : function(e)
25297 Roo.log('img onclick');
25298 this.fireEvent('click', this, e);
25310 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25313 * @class Roo.bootstrap.dash.NumberBox
25314 * @extends Roo.bootstrap.Component
25315 * Bootstrap NumberBox class
25316 * @cfg {String} headline Box headline
25317 * @cfg {String} content Box content
25318 * @cfg {String} icon Box icon
25319 * @cfg {String} footer Footer text
25320 * @cfg {String} fhref Footer href
25323 * Create a new NumberBox
25324 * @param {Object} config The config object
25328 Roo.bootstrap.dash.NumberBox = function(config){
25329 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25333 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25342 getAutoCreate : function(){
25346 cls : 'small-box ',
25354 cls : 'roo-headline',
25355 html : this.headline
25359 cls : 'roo-content',
25360 html : this.content
25374 cls : 'ion ' + this.icon
25383 cls : 'small-box-footer',
25384 href : this.fhref || '#',
25388 cfg.cn.push(footer);
25395 onRender : function(ct,position){
25396 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25403 setHeadline: function (value)
25405 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25408 setFooter: function (value, href)
25410 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25413 this.el.select('a.small-box-footer',true).first().attr('href', href);
25418 setContent: function (value)
25420 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25423 initEvents: function()
25437 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25440 * @class Roo.bootstrap.dash.TabBox
25441 * @extends Roo.bootstrap.Component
25442 * Bootstrap TabBox class
25443 * @cfg {String} title Title of the TabBox
25444 * @cfg {String} icon Icon of the TabBox
25445 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25446 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25449 * Create a new TabBox
25450 * @param {Object} config The config object
25454 Roo.bootstrap.dash.TabBox = function(config){
25455 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25460 * When a pane is added
25461 * @param {Roo.bootstrap.dash.TabPane} pane
25465 * @event activatepane
25466 * When a pane is activated
25467 * @param {Roo.bootstrap.dash.TabPane} pane
25469 "activatepane" : true
25477 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25482 tabScrollable : false,
25484 getChildContainer : function()
25486 return this.el.select('.tab-content', true).first();
25489 getAutoCreate : function(){
25493 cls: 'pull-left header',
25501 cls: 'fa ' + this.icon
25507 cls: 'nav nav-tabs pull-right',
25513 if(this.tabScrollable){
25520 cls: 'nav nav-tabs pull-right',
25531 cls: 'nav-tabs-custom',
25536 cls: 'tab-content no-padding',
25544 initEvents : function()
25546 //Roo.log('add add pane handler');
25547 this.on('addpane', this.onAddPane, this);
25550 * Updates the box title
25551 * @param {String} html to set the title to.
25553 setTitle : function(value)
25555 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25557 onAddPane : function(pane)
25559 this.panes.push(pane);
25560 //Roo.log('addpane');
25562 // tabs are rendere left to right..
25563 if(!this.showtabs){
25567 var ctr = this.el.select('.nav-tabs', true).first();
25570 var existing = ctr.select('.nav-tab',true);
25571 var qty = existing.getCount();;
25574 var tab = ctr.createChild({
25576 cls : 'nav-tab' + (qty ? '' : ' active'),
25584 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25587 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25589 pane.el.addClass('active');
25594 onTabClick : function(ev,un,ob,pane)
25596 //Roo.log('tab - prev default');
25597 ev.preventDefault();
25600 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25601 pane.tab.addClass('active');
25602 //Roo.log(pane.title);
25603 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25604 // technically we should have a deactivate event.. but maybe add later.
25605 // and it should not de-activate the selected tab...
25606 this.fireEvent('activatepane', pane);
25607 pane.el.addClass('active');
25608 pane.fireEvent('activate');
25613 getActivePane : function()
25616 Roo.each(this.panes, function(p) {
25617 if(p.el.hasClass('active')){
25638 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25640 * @class Roo.bootstrap.TabPane
25641 * @extends Roo.bootstrap.Component
25642 * Bootstrap TabPane class
25643 * @cfg {Boolean} active (false | true) Default false
25644 * @cfg {String} title title of panel
25648 * Create a new TabPane
25649 * @param {Object} config The config object
25652 Roo.bootstrap.dash.TabPane = function(config){
25653 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25659 * When a pane is activated
25660 * @param {Roo.bootstrap.dash.TabPane} pane
25667 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25672 // the tabBox that this is attached to.
25675 getAutoCreate : function()
25683 cfg.cls += ' active';
25688 initEvents : function()
25690 //Roo.log('trigger add pane handler');
25691 this.parent().fireEvent('addpane', this)
25695 * Updates the tab title
25696 * @param {String} html to set the title to.
25698 setTitle: function(str)
25704 this.tab.select('a', true).first().dom.innerHTML = str;
25721 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25724 * @class Roo.bootstrap.menu.Menu
25725 * @extends Roo.bootstrap.Component
25726 * Bootstrap Menu class - container for Menu
25727 * @cfg {String} html Text of the menu
25728 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25729 * @cfg {String} icon Font awesome icon
25730 * @cfg {String} pos Menu align to (top | bottom) default bottom
25734 * Create a new Menu
25735 * @param {Object} config The config object
25739 Roo.bootstrap.menu.Menu = function(config){
25740 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25744 * @event beforeshow
25745 * Fires before this menu is displayed
25746 * @param {Roo.bootstrap.menu.Menu} this
25750 * @event beforehide
25751 * Fires before this menu is hidden
25752 * @param {Roo.bootstrap.menu.Menu} this
25757 * Fires after this menu is displayed
25758 * @param {Roo.bootstrap.menu.Menu} this
25763 * Fires after this menu is hidden
25764 * @param {Roo.bootstrap.menu.Menu} this
25769 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25770 * @param {Roo.bootstrap.menu.Menu} this
25771 * @param {Roo.EventObject} e
25778 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25782 weight : 'default',
25787 getChildContainer : function() {
25788 if(this.isSubMenu){
25792 return this.el.select('ul.dropdown-menu', true).first();
25795 getAutoCreate : function()
25800 cls : 'roo-menu-text',
25808 cls : 'fa ' + this.icon
25819 cls : 'dropdown-button btn btn-' + this.weight,
25824 cls : 'dropdown-toggle btn btn-' + this.weight,
25834 cls : 'dropdown-menu'
25840 if(this.pos == 'top'){
25841 cfg.cls += ' dropup';
25844 if(this.isSubMenu){
25847 cls : 'dropdown-menu'
25854 onRender : function(ct, position)
25856 this.isSubMenu = ct.hasClass('dropdown-submenu');
25858 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25861 initEvents : function()
25863 if(this.isSubMenu){
25867 this.hidden = true;
25869 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25870 this.triggerEl.on('click', this.onTriggerPress, this);
25872 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25873 this.buttonEl.on('click', this.onClick, this);
25879 if(this.isSubMenu){
25883 return this.el.select('ul.dropdown-menu', true).first();
25886 onClick : function(e)
25888 this.fireEvent("click", this, e);
25891 onTriggerPress : function(e)
25893 if (this.isVisible()) {
25900 isVisible : function(){
25901 return !this.hidden;
25906 this.fireEvent("beforeshow", this);
25908 this.hidden = false;
25909 this.el.addClass('open');
25911 Roo.get(document).on("mouseup", this.onMouseUp, this);
25913 this.fireEvent("show", this);
25920 this.fireEvent("beforehide", this);
25922 this.hidden = true;
25923 this.el.removeClass('open');
25925 Roo.get(document).un("mouseup", this.onMouseUp);
25927 this.fireEvent("hide", this);
25930 onMouseUp : function()
25944 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25947 * @class Roo.bootstrap.menu.Item
25948 * @extends Roo.bootstrap.Component
25949 * Bootstrap MenuItem class
25950 * @cfg {Boolean} submenu (true | false) default false
25951 * @cfg {String} html text of the item
25952 * @cfg {String} href the link
25953 * @cfg {Boolean} disable (true | false) default false
25954 * @cfg {Boolean} preventDefault (true | false) default true
25955 * @cfg {String} icon Font awesome icon
25956 * @cfg {String} pos Submenu align to (left | right) default right
25960 * Create a new Item
25961 * @param {Object} config The config object
25965 Roo.bootstrap.menu.Item = function(config){
25966 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25970 * Fires when the mouse is hovering over this menu
25971 * @param {Roo.bootstrap.menu.Item} this
25972 * @param {Roo.EventObject} e
25977 * Fires when the mouse exits this menu
25978 * @param {Roo.bootstrap.menu.Item} this
25979 * @param {Roo.EventObject} e
25985 * The raw click event for the entire grid.
25986 * @param {Roo.EventObject} e
25992 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25997 preventDefault: true,
26002 getAutoCreate : function()
26007 cls : 'roo-menu-item-text',
26015 cls : 'fa ' + this.icon
26024 href : this.href || '#',
26031 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26035 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26037 if(this.pos == 'left'){
26038 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26045 initEvents : function()
26047 this.el.on('mouseover', this.onMouseOver, this);
26048 this.el.on('mouseout', this.onMouseOut, this);
26050 this.el.select('a', true).first().on('click', this.onClick, this);
26054 onClick : function(e)
26056 if(this.preventDefault){
26057 e.preventDefault();
26060 this.fireEvent("click", this, e);
26063 onMouseOver : function(e)
26065 if(this.submenu && this.pos == 'left'){
26066 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26069 this.fireEvent("mouseover", this, e);
26072 onMouseOut : function(e)
26074 this.fireEvent("mouseout", this, e);
26086 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26089 * @class Roo.bootstrap.menu.Separator
26090 * @extends Roo.bootstrap.Component
26091 * Bootstrap Separator class
26094 * Create a new Separator
26095 * @param {Object} config The config object
26099 Roo.bootstrap.menu.Separator = function(config){
26100 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26103 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26105 getAutoCreate : function(){
26126 * @class Roo.bootstrap.Tooltip
26127 * Bootstrap Tooltip class
26128 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26129 * to determine which dom element triggers the tooltip.
26131 * It needs to add support for additional attributes like tooltip-position
26134 * Create a new Toolti
26135 * @param {Object} config The config object
26138 Roo.bootstrap.Tooltip = function(config){
26139 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26141 this.alignment = Roo.bootstrap.Tooltip.alignment;
26143 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26144 this.alignment = config.alignment;
26149 Roo.apply(Roo.bootstrap.Tooltip, {
26151 * @function init initialize tooltip monitoring.
26155 currentTip : false,
26156 currentRegion : false,
26162 Roo.get(document).on('mouseover', this.enter ,this);
26163 Roo.get(document).on('mouseout', this.leave, this);
26166 this.currentTip = new Roo.bootstrap.Tooltip();
26169 enter : function(ev)
26171 var dom = ev.getTarget();
26173 //Roo.log(['enter',dom]);
26174 var el = Roo.fly(dom);
26175 if (this.currentEl) {
26177 //Roo.log(this.currentEl);
26178 //Roo.log(this.currentEl.contains(dom));
26179 if (this.currentEl == el) {
26182 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26188 if (this.currentTip.el) {
26189 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26193 if(!el || el.dom == document){
26199 // you can not look for children, as if el is the body.. then everythign is the child..
26200 if (!el.attr('tooltip')) { //
26201 if (!el.select("[tooltip]").elements.length) {
26204 // is the mouse over this child...?
26205 bindEl = el.select("[tooltip]").first();
26206 var xy = ev.getXY();
26207 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26208 //Roo.log("not in region.");
26211 //Roo.log("child element over..");
26214 this.currentEl = bindEl;
26215 this.currentTip.bind(bindEl);
26216 this.currentRegion = Roo.lib.Region.getRegion(dom);
26217 this.currentTip.enter();
26220 leave : function(ev)
26222 var dom = ev.getTarget();
26223 //Roo.log(['leave',dom]);
26224 if (!this.currentEl) {
26229 if (dom != this.currentEl.dom) {
26232 var xy = ev.getXY();
26233 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26236 // only activate leave if mouse cursor is outside... bounding box..
26241 if (this.currentTip) {
26242 this.currentTip.leave();
26244 //Roo.log('clear currentEl');
26245 this.currentEl = false;
26250 'left' : ['r-l', [-2,0], 'right'],
26251 'right' : ['l-r', [2,0], 'left'],
26252 'bottom' : ['t-b', [0,2], 'top'],
26253 'top' : [ 'b-t', [0,-2], 'bottom']
26259 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26264 delay : null, // can be { show : 300 , hide: 500}
26268 hoverState : null, //???
26270 placement : 'bottom',
26274 getAutoCreate : function(){
26281 cls : 'tooltip-arrow'
26284 cls : 'tooltip-inner'
26291 bind : function(el)
26297 enter : function () {
26299 if (this.timeout != null) {
26300 clearTimeout(this.timeout);
26303 this.hoverState = 'in';
26304 //Roo.log("enter - show");
26305 if (!this.delay || !this.delay.show) {
26310 this.timeout = setTimeout(function () {
26311 if (_t.hoverState == 'in') {
26314 }, this.delay.show);
26318 clearTimeout(this.timeout);
26320 this.hoverState = 'out';
26321 if (!this.delay || !this.delay.hide) {
26327 this.timeout = setTimeout(function () {
26328 //Roo.log("leave - timeout");
26330 if (_t.hoverState == 'out') {
26332 Roo.bootstrap.Tooltip.currentEl = false;
26337 show : function (msg)
26340 this.render(document.body);
26343 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26345 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26347 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26349 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26351 var placement = typeof this.placement == 'function' ?
26352 this.placement.call(this, this.el, on_el) :
26355 var autoToken = /\s?auto?\s?/i;
26356 var autoPlace = autoToken.test(placement);
26358 placement = placement.replace(autoToken, '') || 'top';
26362 //this.el.setXY([0,0]);
26364 //this.el.dom.style.display='block';
26366 //this.el.appendTo(on_el);
26368 var p = this.getPosition();
26369 var box = this.el.getBox();
26375 var align = this.alignment[placement];
26377 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26379 if(placement == 'top' || placement == 'bottom'){
26381 placement = 'right';
26384 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26385 placement = 'left';
26388 var scroll = Roo.select('body', true).first().getScroll();
26390 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26394 align = this.alignment[placement];
26397 this.el.alignTo(this.bindEl, align[0],align[1]);
26398 //var arrow = this.el.select('.arrow',true).first();
26399 //arrow.set(align[2],
26401 this.el.addClass(placement);
26403 this.el.addClass('in fade');
26405 this.hoverState = null;
26407 if (this.el.hasClass('fade')) {
26418 //this.el.setXY([0,0]);
26419 this.el.removeClass('in');
26435 * @class Roo.bootstrap.LocationPicker
26436 * @extends Roo.bootstrap.Component
26437 * Bootstrap LocationPicker class
26438 * @cfg {Number} latitude Position when init default 0
26439 * @cfg {Number} longitude Position when init default 0
26440 * @cfg {Number} zoom default 15
26441 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26442 * @cfg {Boolean} mapTypeControl default false
26443 * @cfg {Boolean} disableDoubleClickZoom default false
26444 * @cfg {Boolean} scrollwheel default true
26445 * @cfg {Boolean} streetViewControl default false
26446 * @cfg {Number} radius default 0
26447 * @cfg {String} locationName
26448 * @cfg {Boolean} draggable default true
26449 * @cfg {Boolean} enableAutocomplete default false
26450 * @cfg {Boolean} enableReverseGeocode default true
26451 * @cfg {String} markerTitle
26454 * Create a new LocationPicker
26455 * @param {Object} config The config object
26459 Roo.bootstrap.LocationPicker = function(config){
26461 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26466 * Fires when the picker initialized.
26467 * @param {Roo.bootstrap.LocationPicker} this
26468 * @param {Google Location} location
26472 * @event positionchanged
26473 * Fires when the picker position changed.
26474 * @param {Roo.bootstrap.LocationPicker} this
26475 * @param {Google Location} location
26477 positionchanged : true,
26480 * Fires when the map resize.
26481 * @param {Roo.bootstrap.LocationPicker} this
26486 * Fires when the map show.
26487 * @param {Roo.bootstrap.LocationPicker} this
26492 * Fires when the map hide.
26493 * @param {Roo.bootstrap.LocationPicker} this
26498 * Fires when click the map.
26499 * @param {Roo.bootstrap.LocationPicker} this
26500 * @param {Map event} e
26504 * @event mapRightClick
26505 * Fires when right click the map.
26506 * @param {Roo.bootstrap.LocationPicker} this
26507 * @param {Map event} e
26509 mapRightClick : true,
26511 * @event markerClick
26512 * Fires when click the marker.
26513 * @param {Roo.bootstrap.LocationPicker} this
26514 * @param {Map event} e
26516 markerClick : true,
26518 * @event markerRightClick
26519 * Fires when right click the marker.
26520 * @param {Roo.bootstrap.LocationPicker} this
26521 * @param {Map event} e
26523 markerRightClick : true,
26525 * @event OverlayViewDraw
26526 * Fires when OverlayView Draw
26527 * @param {Roo.bootstrap.LocationPicker} this
26529 OverlayViewDraw : true,
26531 * @event OverlayViewOnAdd
26532 * Fires when OverlayView Draw
26533 * @param {Roo.bootstrap.LocationPicker} this
26535 OverlayViewOnAdd : true,
26537 * @event OverlayViewOnRemove
26538 * Fires when OverlayView Draw
26539 * @param {Roo.bootstrap.LocationPicker} this
26541 OverlayViewOnRemove : true,
26543 * @event OverlayViewShow
26544 * Fires when OverlayView Draw
26545 * @param {Roo.bootstrap.LocationPicker} this
26546 * @param {Pixel} cpx
26548 OverlayViewShow : true,
26550 * @event OverlayViewHide
26551 * Fires when OverlayView Draw
26552 * @param {Roo.bootstrap.LocationPicker} this
26554 OverlayViewHide : true,
26556 * @event loadexception
26557 * Fires when load google lib failed.
26558 * @param {Roo.bootstrap.LocationPicker} this
26560 loadexception : true
26565 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26567 gMapContext: false,
26573 mapTypeControl: false,
26574 disableDoubleClickZoom: false,
26576 streetViewControl: false,
26580 enableAutocomplete: false,
26581 enableReverseGeocode: true,
26584 getAutoCreate: function()
26589 cls: 'roo-location-picker'
26595 initEvents: function(ct, position)
26597 if(!this.el.getWidth() || this.isApplied()){
26601 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26606 initial: function()
26608 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26609 this.fireEvent('loadexception', this);
26613 if(!this.mapTypeId){
26614 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26617 this.gMapContext = this.GMapContext();
26619 this.initOverlayView();
26621 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26625 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26626 _this.setPosition(_this.gMapContext.marker.position);
26629 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26630 _this.fireEvent('mapClick', this, event);
26634 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26635 _this.fireEvent('mapRightClick', this, event);
26639 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26640 _this.fireEvent('markerClick', this, event);
26644 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26645 _this.fireEvent('markerRightClick', this, event);
26649 this.setPosition(this.gMapContext.location);
26651 this.fireEvent('initial', this, this.gMapContext.location);
26654 initOverlayView: function()
26658 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26662 _this.fireEvent('OverlayViewDraw', _this);
26667 _this.fireEvent('OverlayViewOnAdd', _this);
26670 onRemove: function()
26672 _this.fireEvent('OverlayViewOnRemove', _this);
26675 show: function(cpx)
26677 _this.fireEvent('OverlayViewShow', _this, cpx);
26682 _this.fireEvent('OverlayViewHide', _this);
26688 fromLatLngToContainerPixel: function(event)
26690 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26693 isApplied: function()
26695 return this.getGmapContext() == false ? false : true;
26698 getGmapContext: function()
26700 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26703 GMapContext: function()
26705 var position = new google.maps.LatLng(this.latitude, this.longitude);
26707 var _map = new google.maps.Map(this.el.dom, {
26710 mapTypeId: this.mapTypeId,
26711 mapTypeControl: this.mapTypeControl,
26712 disableDoubleClickZoom: this.disableDoubleClickZoom,
26713 scrollwheel: this.scrollwheel,
26714 streetViewControl: this.streetViewControl,
26715 locationName: this.locationName,
26716 draggable: this.draggable,
26717 enableAutocomplete: this.enableAutocomplete,
26718 enableReverseGeocode: this.enableReverseGeocode
26721 var _marker = new google.maps.Marker({
26722 position: position,
26724 title: this.markerTitle,
26725 draggable: this.draggable
26732 location: position,
26733 radius: this.radius,
26734 locationName: this.locationName,
26735 addressComponents: {
26736 formatted_address: null,
26737 addressLine1: null,
26738 addressLine2: null,
26740 streetNumber: null,
26744 stateOrProvince: null
26747 domContainer: this.el.dom,
26748 geodecoder: new google.maps.Geocoder()
26752 drawCircle: function(center, radius, options)
26754 if (this.gMapContext.circle != null) {
26755 this.gMapContext.circle.setMap(null);
26759 options = Roo.apply({}, options, {
26760 strokeColor: "#0000FF",
26761 strokeOpacity: .35,
26763 fillColor: "#0000FF",
26767 options.map = this.gMapContext.map;
26768 options.radius = radius;
26769 options.center = center;
26770 this.gMapContext.circle = new google.maps.Circle(options);
26771 return this.gMapContext.circle;
26777 setPosition: function(location)
26779 this.gMapContext.location = location;
26780 this.gMapContext.marker.setPosition(location);
26781 this.gMapContext.map.panTo(location);
26782 this.drawCircle(location, this.gMapContext.radius, {});
26786 if (this.gMapContext.settings.enableReverseGeocode) {
26787 this.gMapContext.geodecoder.geocode({
26788 latLng: this.gMapContext.location
26789 }, function(results, status) {
26791 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26792 _this.gMapContext.locationName = results[0].formatted_address;
26793 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26795 _this.fireEvent('positionchanged', this, location);
26802 this.fireEvent('positionchanged', this, location);
26807 google.maps.event.trigger(this.gMapContext.map, "resize");
26809 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26811 this.fireEvent('resize', this);
26814 setPositionByLatLng: function(latitude, longitude)
26816 this.setPosition(new google.maps.LatLng(latitude, longitude));
26819 getCurrentPosition: function()
26822 latitude: this.gMapContext.location.lat(),
26823 longitude: this.gMapContext.location.lng()
26827 getAddressName: function()
26829 return this.gMapContext.locationName;
26832 getAddressComponents: function()
26834 return this.gMapContext.addressComponents;
26837 address_component_from_google_geocode: function(address_components)
26841 for (var i = 0; i < address_components.length; i++) {
26842 var component = address_components[i];
26843 if (component.types.indexOf("postal_code") >= 0) {
26844 result.postalCode = component.short_name;
26845 } else if (component.types.indexOf("street_number") >= 0) {
26846 result.streetNumber = component.short_name;
26847 } else if (component.types.indexOf("route") >= 0) {
26848 result.streetName = component.short_name;
26849 } else if (component.types.indexOf("neighborhood") >= 0) {
26850 result.city = component.short_name;
26851 } else if (component.types.indexOf("locality") >= 0) {
26852 result.city = component.short_name;
26853 } else if (component.types.indexOf("sublocality") >= 0) {
26854 result.district = component.short_name;
26855 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26856 result.stateOrProvince = component.short_name;
26857 } else if (component.types.indexOf("country") >= 0) {
26858 result.country = component.short_name;
26862 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26863 result.addressLine2 = "";
26867 setZoomLevel: function(zoom)
26869 this.gMapContext.map.setZoom(zoom);
26882 this.fireEvent('show', this);
26893 this.fireEvent('hide', this);
26898 Roo.apply(Roo.bootstrap.LocationPicker, {
26900 OverlayView : function(map, options)
26902 options = options || {};
26916 * @class Roo.bootstrap.Alert
26917 * @extends Roo.bootstrap.Component
26918 * Bootstrap Alert class
26919 * @cfg {String} title The title of alert
26920 * @cfg {String} html The content of alert
26921 * @cfg {String} weight ( success | info | warning | danger )
26922 * @cfg {String} faicon font-awesomeicon
26925 * Create a new alert
26926 * @param {Object} config The config object
26930 Roo.bootstrap.Alert = function(config){
26931 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26935 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26942 getAutoCreate : function()
26951 cls : 'roo-alert-icon'
26956 cls : 'roo-alert-title',
26961 cls : 'roo-alert-text',
26968 cfg.cn[0].cls += ' fa ' + this.faicon;
26972 cfg.cls += ' alert-' + this.weight;
26978 initEvents: function()
26980 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26983 setTitle : function(str)
26985 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26988 setText : function(str)
26990 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26993 setWeight : function(weight)
26996 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26999 this.weight = weight;
27001 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27004 setIcon : function(icon)
27007 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27010 this.faicon = icon;
27012 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27033 * @class Roo.bootstrap.UploadCropbox
27034 * @extends Roo.bootstrap.Component
27035 * Bootstrap UploadCropbox class
27036 * @cfg {String} emptyText show when image has been loaded
27037 * @cfg {String} rotateNotify show when image too small to rotate
27038 * @cfg {Number} errorTimeout default 3000
27039 * @cfg {Number} minWidth default 300
27040 * @cfg {Number} minHeight default 300
27041 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27042 * @cfg {Boolean} isDocument (true|false) default false
27043 * @cfg {String} url action url
27044 * @cfg {String} paramName default 'imageUpload'
27045 * @cfg {String} method default POST
27046 * @cfg {Boolean} loadMask (true|false) default true
27047 * @cfg {Boolean} loadingText default 'Loading...'
27050 * Create a new UploadCropbox
27051 * @param {Object} config The config object
27054 Roo.bootstrap.UploadCropbox = function(config){
27055 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27059 * @event beforeselectfile
27060 * Fire before select file
27061 * @param {Roo.bootstrap.UploadCropbox} this
27063 "beforeselectfile" : true,
27066 * Fire after initEvent
27067 * @param {Roo.bootstrap.UploadCropbox} this
27072 * Fire after initEvent
27073 * @param {Roo.bootstrap.UploadCropbox} this
27074 * @param {String} data
27079 * Fire when preparing the file data
27080 * @param {Roo.bootstrap.UploadCropbox} this
27081 * @param {Object} file
27086 * Fire when get exception
27087 * @param {Roo.bootstrap.UploadCropbox} this
27088 * @param {XMLHttpRequest} xhr
27090 "exception" : true,
27092 * @event beforeloadcanvas
27093 * Fire before load the canvas
27094 * @param {Roo.bootstrap.UploadCropbox} this
27095 * @param {String} src
27097 "beforeloadcanvas" : true,
27100 * Fire when trash image
27101 * @param {Roo.bootstrap.UploadCropbox} this
27106 * Fire when download the image
27107 * @param {Roo.bootstrap.UploadCropbox} this
27111 * @event footerbuttonclick
27112 * Fire when footerbuttonclick
27113 * @param {Roo.bootstrap.UploadCropbox} this
27114 * @param {String} type
27116 "footerbuttonclick" : true,
27120 * @param {Roo.bootstrap.UploadCropbox} this
27125 * Fire when rotate the image
27126 * @param {Roo.bootstrap.UploadCropbox} this
27127 * @param {String} pos
27132 * Fire when inspect the file
27133 * @param {Roo.bootstrap.UploadCropbox} this
27134 * @param {Object} file
27139 * Fire when xhr upload the file
27140 * @param {Roo.bootstrap.UploadCropbox} this
27141 * @param {Object} data
27146 * Fire when arrange the file data
27147 * @param {Roo.bootstrap.UploadCropbox} this
27148 * @param {Object} formData
27153 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27156 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27158 emptyText : 'Click to upload image',
27159 rotateNotify : 'Image is too small to rotate',
27160 errorTimeout : 3000,
27174 cropType : 'image/jpeg',
27176 canvasLoaded : false,
27177 isDocument : false,
27179 paramName : 'imageUpload',
27181 loadingText : 'Loading...',
27184 getAutoCreate : function()
27188 cls : 'roo-upload-cropbox',
27192 cls : 'roo-upload-cropbox-selector',
27197 cls : 'roo-upload-cropbox-body',
27198 style : 'cursor:pointer',
27202 cls : 'roo-upload-cropbox-preview'
27206 cls : 'roo-upload-cropbox-thumb'
27210 cls : 'roo-upload-cropbox-empty-notify',
27211 html : this.emptyText
27215 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27216 html : this.rotateNotify
27222 cls : 'roo-upload-cropbox-footer',
27225 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27235 onRender : function(ct, position)
27237 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27239 if (this.buttons.length) {
27241 Roo.each(this.buttons, function(bb) {
27243 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27245 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27251 this.maskEl = this.el;
27255 initEvents : function()
27257 this.urlAPI = (window.createObjectURL && window) ||
27258 (window.URL && URL.revokeObjectURL && URL) ||
27259 (window.webkitURL && webkitURL);
27261 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27262 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27264 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27265 this.selectorEl.hide();
27267 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27268 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27270 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27271 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27272 this.thumbEl.hide();
27274 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27275 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27277 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27278 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27279 this.errorEl.hide();
27281 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27282 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27283 this.footerEl.hide();
27285 this.setThumbBoxSize();
27291 this.fireEvent('initial', this);
27298 window.addEventListener("resize", function() { _this.resize(); } );
27300 this.bodyEl.on('click', this.beforeSelectFile, this);
27303 this.bodyEl.on('touchstart', this.onTouchStart, this);
27304 this.bodyEl.on('touchmove', this.onTouchMove, this);
27305 this.bodyEl.on('touchend', this.onTouchEnd, this);
27309 this.bodyEl.on('mousedown', this.onMouseDown, this);
27310 this.bodyEl.on('mousemove', this.onMouseMove, this);
27311 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27312 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27313 Roo.get(document).on('mouseup', this.onMouseUp, this);
27316 this.selectorEl.on('change', this.onFileSelected, this);
27322 this.baseScale = 1;
27324 this.baseRotate = 1;
27325 this.dragable = false;
27326 this.pinching = false;
27329 this.cropData = false;
27330 this.notifyEl.dom.innerHTML = this.emptyText;
27332 this.selectorEl.dom.value = '';
27336 resize : function()
27338 if(this.fireEvent('resize', this) != false){
27339 this.setThumbBoxPosition();
27340 this.setCanvasPosition();
27344 onFooterButtonClick : function(e, el, o, type)
27347 case 'rotate-left' :
27348 this.onRotateLeft(e);
27350 case 'rotate-right' :
27351 this.onRotateRight(e);
27354 this.beforeSelectFile(e);
27369 this.fireEvent('footerbuttonclick', this, type);
27372 beforeSelectFile : function(e)
27374 e.preventDefault();
27376 if(this.fireEvent('beforeselectfile', this) != false){
27377 this.selectorEl.dom.click();
27381 onFileSelected : function(e)
27383 e.preventDefault();
27385 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27389 var file = this.selectorEl.dom.files[0];
27391 if(this.fireEvent('inspect', this, file) != false){
27392 this.prepare(file);
27397 trash : function(e)
27399 this.fireEvent('trash', this);
27402 download : function(e)
27404 this.fireEvent('download', this);
27407 loadCanvas : function(src)
27409 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27413 this.imageEl = document.createElement('img');
27417 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27419 this.imageEl.src = src;
27423 onLoadCanvas : function()
27425 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27426 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27428 this.bodyEl.un('click', this.beforeSelectFile, this);
27430 this.notifyEl.hide();
27431 this.thumbEl.show();
27432 this.footerEl.show();
27434 this.baseRotateLevel();
27436 if(this.isDocument){
27437 this.setThumbBoxSize();
27440 this.setThumbBoxPosition();
27442 this.baseScaleLevel();
27448 this.canvasLoaded = true;
27451 this.maskEl.unmask();
27456 setCanvasPosition : function()
27458 if(!this.canvasEl){
27462 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27463 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27465 this.previewEl.setLeft(pw);
27466 this.previewEl.setTop(ph);
27470 onMouseDown : function(e)
27474 this.dragable = true;
27475 this.pinching = false;
27477 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27478 this.dragable = false;
27482 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27483 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27487 onMouseMove : function(e)
27491 if(!this.canvasLoaded){
27495 if (!this.dragable){
27499 var minX = Math.ceil(this.thumbEl.getLeft(true));
27500 var minY = Math.ceil(this.thumbEl.getTop(true));
27502 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27503 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27505 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27506 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27508 x = x - this.mouseX;
27509 y = y - this.mouseY;
27511 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27512 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27514 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27515 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27517 this.previewEl.setLeft(bgX);
27518 this.previewEl.setTop(bgY);
27520 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27521 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27524 onMouseUp : function(e)
27528 this.dragable = false;
27531 onMouseWheel : function(e)
27535 this.startScale = this.scale;
27537 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27539 if(!this.zoomable()){
27540 this.scale = this.startScale;
27549 zoomable : function()
27551 var minScale = this.thumbEl.getWidth() / this.minWidth;
27553 if(this.minWidth < this.minHeight){
27554 minScale = this.thumbEl.getHeight() / this.minHeight;
27557 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27558 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27562 (this.rotate == 0 || this.rotate == 180) &&
27564 width > this.imageEl.OriginWidth ||
27565 height > this.imageEl.OriginHeight ||
27566 (width < this.minWidth && height < this.minHeight)
27574 (this.rotate == 90 || this.rotate == 270) &&
27576 width > this.imageEl.OriginWidth ||
27577 height > this.imageEl.OriginHeight ||
27578 (width < this.minHeight && height < this.minWidth)
27585 !this.isDocument &&
27586 (this.rotate == 0 || this.rotate == 180) &&
27588 width < this.minWidth ||
27589 width > this.imageEl.OriginWidth ||
27590 height < this.minHeight ||
27591 height > this.imageEl.OriginHeight
27598 !this.isDocument &&
27599 (this.rotate == 90 || this.rotate == 270) &&
27601 width < this.minHeight ||
27602 width > this.imageEl.OriginWidth ||
27603 height < this.minWidth ||
27604 height > this.imageEl.OriginHeight
27614 onRotateLeft : function(e)
27616 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27618 var minScale = this.thumbEl.getWidth() / this.minWidth;
27620 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27621 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27623 this.startScale = this.scale;
27625 while (this.getScaleLevel() < minScale){
27627 this.scale = this.scale + 1;
27629 if(!this.zoomable()){
27634 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27635 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27640 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27647 this.scale = this.startScale;
27649 this.onRotateFail();
27654 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27656 if(this.isDocument){
27657 this.setThumbBoxSize();
27658 this.setThumbBoxPosition();
27659 this.setCanvasPosition();
27664 this.fireEvent('rotate', this, 'left');
27668 onRotateRight : function(e)
27670 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27672 var minScale = this.thumbEl.getWidth() / this.minWidth;
27674 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27675 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27677 this.startScale = this.scale;
27679 while (this.getScaleLevel() < minScale){
27681 this.scale = this.scale + 1;
27683 if(!this.zoomable()){
27688 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27689 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27694 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27701 this.scale = this.startScale;
27703 this.onRotateFail();
27708 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27710 if(this.isDocument){
27711 this.setThumbBoxSize();
27712 this.setThumbBoxPosition();
27713 this.setCanvasPosition();
27718 this.fireEvent('rotate', this, 'right');
27721 onRotateFail : function()
27723 this.errorEl.show(true);
27727 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27732 this.previewEl.dom.innerHTML = '';
27734 var canvasEl = document.createElement("canvas");
27736 var contextEl = canvasEl.getContext("2d");
27738 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27739 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27740 var center = this.imageEl.OriginWidth / 2;
27742 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27743 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27744 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27745 center = this.imageEl.OriginHeight / 2;
27748 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27750 contextEl.translate(center, center);
27751 contextEl.rotate(this.rotate * Math.PI / 180);
27753 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27755 this.canvasEl = document.createElement("canvas");
27757 this.contextEl = this.canvasEl.getContext("2d");
27759 switch (this.rotate) {
27762 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27763 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27765 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27770 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27771 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27773 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27774 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);
27778 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27783 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27784 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27786 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27787 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);
27791 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);
27796 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27797 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27799 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27800 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27804 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);
27811 this.previewEl.appendChild(this.canvasEl);
27813 this.setCanvasPosition();
27818 if(!this.canvasLoaded){
27822 var imageCanvas = document.createElement("canvas");
27824 var imageContext = imageCanvas.getContext("2d");
27826 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27827 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27829 var center = imageCanvas.width / 2;
27831 imageContext.translate(center, center);
27833 imageContext.rotate(this.rotate * Math.PI / 180);
27835 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27837 var canvas = document.createElement("canvas");
27839 var context = canvas.getContext("2d");
27841 canvas.width = this.minWidth;
27842 canvas.height = this.minHeight;
27844 switch (this.rotate) {
27847 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27848 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27850 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27851 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27853 var targetWidth = this.minWidth - 2 * x;
27854 var targetHeight = this.minHeight - 2 * y;
27858 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27859 scale = targetWidth / width;
27862 if(x > 0 && y == 0){
27863 scale = targetHeight / height;
27866 if(x > 0 && y > 0){
27867 scale = targetWidth / width;
27869 if(width < height){
27870 scale = targetHeight / height;
27874 context.scale(scale, scale);
27876 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27877 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27879 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27880 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27882 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27887 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27888 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27890 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27891 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27893 var targetWidth = this.minWidth - 2 * x;
27894 var targetHeight = this.minHeight - 2 * y;
27898 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27899 scale = targetWidth / width;
27902 if(x > 0 && y == 0){
27903 scale = targetHeight / height;
27906 if(x > 0 && y > 0){
27907 scale = targetWidth / width;
27909 if(width < height){
27910 scale = targetHeight / height;
27914 context.scale(scale, scale);
27916 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27917 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27919 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27920 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27922 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27924 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27929 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27930 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27932 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27933 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27935 var targetWidth = this.minWidth - 2 * x;
27936 var targetHeight = this.minHeight - 2 * y;
27940 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27941 scale = targetWidth / width;
27944 if(x > 0 && y == 0){
27945 scale = targetHeight / height;
27948 if(x > 0 && y > 0){
27949 scale = targetWidth / width;
27951 if(width < height){
27952 scale = targetHeight / height;
27956 context.scale(scale, scale);
27958 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27959 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27961 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27962 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27964 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27965 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27967 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27972 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27973 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27975 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27976 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27978 var targetWidth = this.minWidth - 2 * x;
27979 var targetHeight = this.minHeight - 2 * y;
27983 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27984 scale = targetWidth / width;
27987 if(x > 0 && y == 0){
27988 scale = targetHeight / height;
27991 if(x > 0 && y > 0){
27992 scale = targetWidth / width;
27994 if(width < height){
27995 scale = targetHeight / height;
27999 context.scale(scale, scale);
28001 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28002 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28004 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28005 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28007 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28009 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28016 this.cropData = canvas.toDataURL(this.cropType);
28018 if(this.fireEvent('crop', this, this.cropData) !== false){
28019 this.process(this.file, this.cropData);
28026 setThumbBoxSize : function()
28030 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28031 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28032 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28034 this.minWidth = width;
28035 this.minHeight = height;
28037 if(this.rotate == 90 || this.rotate == 270){
28038 this.minWidth = height;
28039 this.minHeight = width;
28044 width = Math.ceil(this.minWidth * height / this.minHeight);
28046 if(this.minWidth > this.minHeight){
28048 height = Math.ceil(this.minHeight * width / this.minWidth);
28051 this.thumbEl.setStyle({
28052 width : width + 'px',
28053 height : height + 'px'
28060 setThumbBoxPosition : function()
28062 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28063 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28065 this.thumbEl.setLeft(x);
28066 this.thumbEl.setTop(y);
28070 baseRotateLevel : function()
28072 this.baseRotate = 1;
28075 typeof(this.exif) != 'undefined' &&
28076 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28077 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28079 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28082 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28086 baseScaleLevel : function()
28090 if(this.isDocument){
28092 if(this.baseRotate == 6 || this.baseRotate == 8){
28094 height = this.thumbEl.getHeight();
28095 this.baseScale = height / this.imageEl.OriginWidth;
28097 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28098 width = this.thumbEl.getWidth();
28099 this.baseScale = width / this.imageEl.OriginHeight;
28105 height = this.thumbEl.getHeight();
28106 this.baseScale = height / this.imageEl.OriginHeight;
28108 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28109 width = this.thumbEl.getWidth();
28110 this.baseScale = width / this.imageEl.OriginWidth;
28116 if(this.baseRotate == 6 || this.baseRotate == 8){
28118 width = this.thumbEl.getHeight();
28119 this.baseScale = width / this.imageEl.OriginHeight;
28121 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28122 height = this.thumbEl.getWidth();
28123 this.baseScale = height / this.imageEl.OriginHeight;
28126 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28127 height = this.thumbEl.getWidth();
28128 this.baseScale = height / this.imageEl.OriginHeight;
28130 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28131 width = this.thumbEl.getHeight();
28132 this.baseScale = width / this.imageEl.OriginWidth;
28139 width = this.thumbEl.getWidth();
28140 this.baseScale = width / this.imageEl.OriginWidth;
28142 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28143 height = this.thumbEl.getHeight();
28144 this.baseScale = height / this.imageEl.OriginHeight;
28147 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28149 height = this.thumbEl.getHeight();
28150 this.baseScale = height / this.imageEl.OriginHeight;
28152 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28153 width = this.thumbEl.getWidth();
28154 this.baseScale = width / this.imageEl.OriginWidth;
28162 getScaleLevel : function()
28164 return this.baseScale * Math.pow(1.1, this.scale);
28167 onTouchStart : function(e)
28169 if(!this.canvasLoaded){
28170 this.beforeSelectFile(e);
28174 var touches = e.browserEvent.touches;
28180 if(touches.length == 1){
28181 this.onMouseDown(e);
28185 if(touches.length != 2){
28191 for(var i = 0, finger; finger = touches[i]; i++){
28192 coords.push(finger.pageX, finger.pageY);
28195 var x = Math.pow(coords[0] - coords[2], 2);
28196 var y = Math.pow(coords[1] - coords[3], 2);
28198 this.startDistance = Math.sqrt(x + y);
28200 this.startScale = this.scale;
28202 this.pinching = true;
28203 this.dragable = false;
28207 onTouchMove : function(e)
28209 if(!this.pinching && !this.dragable){
28213 var touches = e.browserEvent.touches;
28220 this.onMouseMove(e);
28226 for(var i = 0, finger; finger = touches[i]; i++){
28227 coords.push(finger.pageX, finger.pageY);
28230 var x = Math.pow(coords[0] - coords[2], 2);
28231 var y = Math.pow(coords[1] - coords[3], 2);
28233 this.endDistance = Math.sqrt(x + y);
28235 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28237 if(!this.zoomable()){
28238 this.scale = this.startScale;
28246 onTouchEnd : function(e)
28248 this.pinching = false;
28249 this.dragable = false;
28253 process : function(file, crop)
28256 this.maskEl.mask(this.loadingText);
28259 this.xhr = new XMLHttpRequest();
28261 file.xhr = this.xhr;
28263 this.xhr.open(this.method, this.url, true);
28266 "Accept": "application/json",
28267 "Cache-Control": "no-cache",
28268 "X-Requested-With": "XMLHttpRequest"
28271 for (var headerName in headers) {
28272 var headerValue = headers[headerName];
28274 this.xhr.setRequestHeader(headerName, headerValue);
28280 this.xhr.onload = function()
28282 _this.xhrOnLoad(_this.xhr);
28285 this.xhr.onerror = function()
28287 _this.xhrOnError(_this.xhr);
28290 var formData = new FormData();
28292 formData.append('returnHTML', 'NO');
28295 formData.append('crop', crop);
28298 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28299 formData.append(this.paramName, file, file.name);
28302 if(typeof(file.filename) != 'undefined'){
28303 formData.append('filename', file.filename);
28306 if(typeof(file.mimetype) != 'undefined'){
28307 formData.append('mimetype', file.mimetype);
28310 if(this.fireEvent('arrange', this, formData) != false){
28311 this.xhr.send(formData);
28315 xhrOnLoad : function(xhr)
28318 this.maskEl.unmask();
28321 if (xhr.readyState !== 4) {
28322 this.fireEvent('exception', this, xhr);
28326 var response = Roo.decode(xhr.responseText);
28328 if(!response.success){
28329 this.fireEvent('exception', this, xhr);
28333 var response = Roo.decode(xhr.responseText);
28335 this.fireEvent('upload', this, response);
28339 xhrOnError : function()
28342 this.maskEl.unmask();
28345 Roo.log('xhr on error');
28347 var response = Roo.decode(xhr.responseText);
28353 prepare : function(file)
28356 this.maskEl.mask(this.loadingText);
28362 if(typeof(file) === 'string'){
28363 this.loadCanvas(file);
28367 if(!file || !this.urlAPI){
28372 this.cropType = file.type;
28376 if(this.fireEvent('prepare', this, this.file) != false){
28378 var reader = new FileReader();
28380 reader.onload = function (e) {
28381 if (e.target.error) {
28382 Roo.log(e.target.error);
28386 var buffer = e.target.result,
28387 dataView = new DataView(buffer),
28389 maxOffset = dataView.byteLength - 4,
28393 if (dataView.getUint16(0) === 0xffd8) {
28394 while (offset < maxOffset) {
28395 markerBytes = dataView.getUint16(offset);
28397 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28398 markerLength = dataView.getUint16(offset + 2) + 2;
28399 if (offset + markerLength > dataView.byteLength) {
28400 Roo.log('Invalid meta data: Invalid segment size.');
28404 if(markerBytes == 0xffe1){
28405 _this.parseExifData(
28412 offset += markerLength;
28422 var url = _this.urlAPI.createObjectURL(_this.file);
28424 _this.loadCanvas(url);
28429 reader.readAsArrayBuffer(this.file);
28435 parseExifData : function(dataView, offset, length)
28437 var tiffOffset = offset + 10,
28441 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28442 // No Exif data, might be XMP data instead
28446 // Check for the ASCII code for "Exif" (0x45786966):
28447 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28448 // No Exif data, might be XMP data instead
28451 if (tiffOffset + 8 > dataView.byteLength) {
28452 Roo.log('Invalid Exif data: Invalid segment size.');
28455 // Check for the two null bytes:
28456 if (dataView.getUint16(offset + 8) !== 0x0000) {
28457 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28460 // Check the byte alignment:
28461 switch (dataView.getUint16(tiffOffset)) {
28463 littleEndian = true;
28466 littleEndian = false;
28469 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28472 // Check for the TIFF tag marker (0x002A):
28473 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28474 Roo.log('Invalid Exif data: Missing TIFF marker.');
28477 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28478 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28480 this.parseExifTags(
28483 tiffOffset + dirOffset,
28488 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28493 if (dirOffset + 6 > dataView.byteLength) {
28494 Roo.log('Invalid Exif data: Invalid directory offset.');
28497 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28498 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28499 if (dirEndOffset + 4 > dataView.byteLength) {
28500 Roo.log('Invalid Exif data: Invalid directory size.');
28503 for (i = 0; i < tagsNumber; i += 1) {
28507 dirOffset + 2 + 12 * i, // tag offset
28511 // Return the offset to the next directory:
28512 return dataView.getUint32(dirEndOffset, littleEndian);
28515 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28517 var tag = dataView.getUint16(offset, littleEndian);
28519 this.exif[tag] = this.getExifValue(
28523 dataView.getUint16(offset + 2, littleEndian), // tag type
28524 dataView.getUint32(offset + 4, littleEndian), // tag length
28529 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28531 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28540 Roo.log('Invalid Exif data: Invalid tag type.');
28544 tagSize = tagType.size * length;
28545 // Determine if the value is contained in the dataOffset bytes,
28546 // or if the value at the dataOffset is a pointer to the actual data:
28547 dataOffset = tagSize > 4 ?
28548 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28549 if (dataOffset + tagSize > dataView.byteLength) {
28550 Roo.log('Invalid Exif data: Invalid data offset.');
28553 if (length === 1) {
28554 return tagType.getValue(dataView, dataOffset, littleEndian);
28557 for (i = 0; i < length; i += 1) {
28558 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28561 if (tagType.ascii) {
28563 // Concatenate the chars:
28564 for (i = 0; i < values.length; i += 1) {
28566 // Ignore the terminating NULL byte(s):
28567 if (c === '\u0000') {
28579 Roo.apply(Roo.bootstrap.UploadCropbox, {
28581 'Orientation': 0x0112
28585 1: 0, //'top-left',
28587 3: 180, //'bottom-right',
28588 // 4: 'bottom-left',
28590 6: 90, //'right-top',
28591 // 7: 'right-bottom',
28592 8: 270 //'left-bottom'
28596 // byte, 8-bit unsigned int:
28598 getValue: function (dataView, dataOffset) {
28599 return dataView.getUint8(dataOffset);
28603 // ascii, 8-bit byte:
28605 getValue: function (dataView, dataOffset) {
28606 return String.fromCharCode(dataView.getUint8(dataOffset));
28611 // short, 16 bit int:
28613 getValue: function (dataView, dataOffset, littleEndian) {
28614 return dataView.getUint16(dataOffset, littleEndian);
28618 // long, 32 bit int:
28620 getValue: function (dataView, dataOffset, littleEndian) {
28621 return dataView.getUint32(dataOffset, littleEndian);
28625 // rational = two long values, first is numerator, second is denominator:
28627 getValue: function (dataView, dataOffset, littleEndian) {
28628 return dataView.getUint32(dataOffset, littleEndian) /
28629 dataView.getUint32(dataOffset + 4, littleEndian);
28633 // slong, 32 bit signed int:
28635 getValue: function (dataView, dataOffset, littleEndian) {
28636 return dataView.getInt32(dataOffset, littleEndian);
28640 // srational, two slongs, first is numerator, second is denominator:
28642 getValue: function (dataView, dataOffset, littleEndian) {
28643 return dataView.getInt32(dataOffset, littleEndian) /
28644 dataView.getInt32(dataOffset + 4, littleEndian);
28654 cls : 'btn-group roo-upload-cropbox-rotate-left',
28655 action : 'rotate-left',
28659 cls : 'btn btn-default',
28660 html : '<i class="fa fa-undo"></i>'
28666 cls : 'btn-group roo-upload-cropbox-picture',
28667 action : 'picture',
28671 cls : 'btn btn-default',
28672 html : '<i class="fa fa-picture-o"></i>'
28678 cls : 'btn-group roo-upload-cropbox-rotate-right',
28679 action : 'rotate-right',
28683 cls : 'btn btn-default',
28684 html : '<i class="fa fa-repeat"></i>'
28692 cls : 'btn-group roo-upload-cropbox-rotate-left',
28693 action : 'rotate-left',
28697 cls : 'btn btn-default',
28698 html : '<i class="fa fa-undo"></i>'
28704 cls : 'btn-group roo-upload-cropbox-download',
28705 action : 'download',
28709 cls : 'btn btn-default',
28710 html : '<i class="fa fa-download"></i>'
28716 cls : 'btn-group roo-upload-cropbox-crop',
28721 cls : 'btn btn-default',
28722 html : '<i class="fa fa-crop"></i>'
28728 cls : 'btn-group roo-upload-cropbox-trash',
28733 cls : 'btn btn-default',
28734 html : '<i class="fa fa-trash"></i>'
28740 cls : 'btn-group roo-upload-cropbox-rotate-right',
28741 action : 'rotate-right',
28745 cls : 'btn btn-default',
28746 html : '<i class="fa fa-repeat"></i>'
28754 cls : 'btn-group roo-upload-cropbox-rotate-left',
28755 action : 'rotate-left',
28759 cls : 'btn btn-default',
28760 html : '<i class="fa fa-undo"></i>'
28766 cls : 'btn-group roo-upload-cropbox-rotate-right',
28767 action : 'rotate-right',
28771 cls : 'btn btn-default',
28772 html : '<i class="fa fa-repeat"></i>'
28785 * @class Roo.bootstrap.DocumentManager
28786 * @extends Roo.bootstrap.Component
28787 * Bootstrap DocumentManager class
28788 * @cfg {String} paramName default 'imageUpload'
28789 * @cfg {String} toolTipName default 'filename'
28790 * @cfg {String} method default POST
28791 * @cfg {String} url action url
28792 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28793 * @cfg {Boolean} multiple multiple upload default true
28794 * @cfg {Number} thumbSize default 300
28795 * @cfg {String} fieldLabel
28796 * @cfg {Number} labelWidth default 4
28797 * @cfg {String} labelAlign (left|top) default left
28798 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28799 * @cfg {Number} labellg set the width of label (1-12)
28800 * @cfg {Number} labelmd set the width of label (1-12)
28801 * @cfg {Number} labelsm set the width of label (1-12)
28802 * @cfg {Number} labelxs set the width of label (1-12)
28805 * Create a new DocumentManager
28806 * @param {Object} config The config object
28809 Roo.bootstrap.DocumentManager = function(config){
28810 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28813 this.delegates = [];
28818 * Fire when initial the DocumentManager
28819 * @param {Roo.bootstrap.DocumentManager} this
28824 * inspect selected file
28825 * @param {Roo.bootstrap.DocumentManager} this
28826 * @param {File} file
28831 * Fire when xhr load exception
28832 * @param {Roo.bootstrap.DocumentManager} this
28833 * @param {XMLHttpRequest} xhr
28835 "exception" : true,
28837 * @event afterupload
28838 * Fire when xhr load exception
28839 * @param {Roo.bootstrap.DocumentManager} this
28840 * @param {XMLHttpRequest} xhr
28842 "afterupload" : true,
28845 * prepare the form data
28846 * @param {Roo.bootstrap.DocumentManager} this
28847 * @param {Object} formData
28852 * Fire when remove the file
28853 * @param {Roo.bootstrap.DocumentManager} this
28854 * @param {Object} file
28859 * Fire after refresh the file
28860 * @param {Roo.bootstrap.DocumentManager} this
28865 * Fire after click the image
28866 * @param {Roo.bootstrap.DocumentManager} this
28867 * @param {Object} file
28872 * Fire when upload a image and editable set to true
28873 * @param {Roo.bootstrap.DocumentManager} this
28874 * @param {Object} file
28878 * @event beforeselectfile
28879 * Fire before select file
28880 * @param {Roo.bootstrap.DocumentManager} this
28882 "beforeselectfile" : true,
28885 * Fire before process file
28886 * @param {Roo.bootstrap.DocumentManager} this
28887 * @param {Object} file
28891 * @event previewrendered
28892 * Fire when preview rendered
28893 * @param {Roo.bootstrap.DocumentManager} this
28894 * @param {Object} file
28896 "previewrendered" : true,
28899 "previewResize" : true
28904 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28913 paramName : 'imageUpload',
28914 toolTipName : 'filename',
28917 labelAlign : 'left',
28927 getAutoCreate : function()
28929 var managerWidget = {
28931 cls : 'roo-document-manager',
28935 cls : 'roo-document-manager-selector',
28940 cls : 'roo-document-manager-uploader',
28944 cls : 'roo-document-manager-upload-btn',
28945 html : '<i class="fa fa-plus"></i>'
28956 cls : 'column col-md-12',
28961 if(this.fieldLabel.length){
28966 cls : 'column col-md-12',
28967 html : this.fieldLabel
28971 cls : 'column col-md-12',
28976 if(this.labelAlign == 'left'){
28981 html : this.fieldLabel
28990 if(this.labelWidth > 12){
28991 content[0].style = "width: " + this.labelWidth + 'px';
28994 if(this.labelWidth < 13 && this.labelmd == 0){
28995 this.labelmd = this.labelWidth;
28998 if(this.labellg > 0){
28999 content[0].cls += ' col-lg-' + this.labellg;
29000 content[1].cls += ' col-lg-' + (12 - this.labellg);
29003 if(this.labelmd > 0){
29004 content[0].cls += ' col-md-' + this.labelmd;
29005 content[1].cls += ' col-md-' + (12 - this.labelmd);
29008 if(this.labelsm > 0){
29009 content[0].cls += ' col-sm-' + this.labelsm;
29010 content[1].cls += ' col-sm-' + (12 - this.labelsm);
29013 if(this.labelxs > 0){
29014 content[0].cls += ' col-xs-' + this.labelxs;
29015 content[1].cls += ' col-xs-' + (12 - this.labelxs);
29023 cls : 'row clearfix',
29031 initEvents : function()
29033 this.managerEl = this.el.select('.roo-document-manager', true).first();
29034 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29036 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29037 this.selectorEl.hide();
29040 this.selectorEl.attr('multiple', 'multiple');
29043 this.selectorEl.on('change', this.onFileSelected, this);
29045 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29046 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29048 this.uploader.on('click', this.onUploaderClick, this);
29050 this.renderProgressDialog();
29054 window.addEventListener("resize", function() { _this.refresh(); } );
29056 this.fireEvent('initial', this);
29059 renderProgressDialog : function()
29063 this.progressDialog = new Roo.bootstrap.Modal({
29064 cls : 'roo-document-manager-progress-dialog',
29065 allow_close : false,
29075 btnclick : function() {
29076 _this.uploadCancel();
29082 this.progressDialog.render(Roo.get(document.body));
29084 this.progress = new Roo.bootstrap.Progress({
29085 cls : 'roo-document-manager-progress',
29090 this.progress.render(this.progressDialog.getChildContainer());
29092 this.progressBar = new Roo.bootstrap.ProgressBar({
29093 cls : 'roo-document-manager-progress-bar',
29096 aria_valuemax : 12,
29100 this.progressBar.render(this.progress.getChildContainer());
29103 onUploaderClick : function(e)
29105 e.preventDefault();
29107 if(this.fireEvent('beforeselectfile', this) != false){
29108 this.selectorEl.dom.click();
29113 onFileSelected : function(e)
29115 e.preventDefault();
29117 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29121 Roo.each(this.selectorEl.dom.files, function(file){
29122 if(this.fireEvent('inspect', this, file) != false){
29123 this.files.push(file);
29133 this.selectorEl.dom.value = '';
29135 if(!this.files || !this.files.length){
29139 if(this.boxes > 0 && this.files.length > this.boxes){
29140 this.files = this.files.slice(0, this.boxes);
29143 this.uploader.show();
29145 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29146 this.uploader.hide();
29155 Roo.each(this.files, function(file){
29157 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29158 var f = this.renderPreview(file);
29163 if(file.type.indexOf('image') != -1){
29164 this.delegates.push(
29166 _this.process(file);
29167 }).createDelegate(this)
29175 _this.process(file);
29176 }).createDelegate(this)
29181 this.files = files;
29183 this.delegates = this.delegates.concat(docs);
29185 if(!this.delegates.length){
29190 this.progressBar.aria_valuemax = this.delegates.length;
29197 arrange : function()
29199 if(!this.delegates.length){
29200 this.progressDialog.hide();
29205 var delegate = this.delegates.shift();
29207 this.progressDialog.show();
29209 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29211 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29216 refresh : function()
29218 this.uploader.show();
29220 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29221 this.uploader.hide();
29224 Roo.isTouch ? this.closable(false) : this.closable(true);
29226 this.fireEvent('refresh', this);
29229 onRemove : function(e, el, o)
29231 e.preventDefault();
29233 this.fireEvent('remove', this, o);
29237 remove : function(o)
29241 Roo.each(this.files, function(file){
29242 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29251 this.files = files;
29258 Roo.each(this.files, function(file){
29263 file.target.remove();
29272 onClick : function(e, el, o)
29274 e.preventDefault();
29276 this.fireEvent('click', this, o);
29280 closable : function(closable)
29282 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29284 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29296 xhrOnLoad : function(xhr)
29298 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29302 if (xhr.readyState !== 4) {
29304 this.fireEvent('exception', this, xhr);
29308 var response = Roo.decode(xhr.responseText);
29310 if(!response.success){
29312 this.fireEvent('exception', this, xhr);
29316 var file = this.renderPreview(response.data);
29318 this.files.push(file);
29322 this.fireEvent('afterupload', this, xhr);
29326 xhrOnError : function(xhr)
29328 Roo.log('xhr on error');
29330 var response = Roo.decode(xhr.responseText);
29337 process : function(file)
29339 if(this.fireEvent('process', this, file) !== false){
29340 if(this.editable && file.type.indexOf('image') != -1){
29341 this.fireEvent('edit', this, file);
29345 this.uploadStart(file, false);
29352 uploadStart : function(file, crop)
29354 this.xhr = new XMLHttpRequest();
29356 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29361 file.xhr = this.xhr;
29363 this.managerEl.createChild({
29365 cls : 'roo-document-manager-loading',
29369 tooltip : file.name,
29370 cls : 'roo-document-manager-thumb',
29371 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29377 this.xhr.open(this.method, this.url, true);
29380 "Accept": "application/json",
29381 "Cache-Control": "no-cache",
29382 "X-Requested-With": "XMLHttpRequest"
29385 for (var headerName in headers) {
29386 var headerValue = headers[headerName];
29388 this.xhr.setRequestHeader(headerName, headerValue);
29394 this.xhr.onload = function()
29396 _this.xhrOnLoad(_this.xhr);
29399 this.xhr.onerror = function()
29401 _this.xhrOnError(_this.xhr);
29404 var formData = new FormData();
29406 formData.append('returnHTML', 'NO');
29409 formData.append('crop', crop);
29412 formData.append(this.paramName, file, file.name);
29419 if(this.fireEvent('prepare', this, formData, options) != false){
29421 if(options.manually){
29425 this.xhr.send(formData);
29429 this.uploadCancel();
29432 uploadCancel : function()
29438 this.delegates = [];
29440 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29447 renderPreview : function(file)
29449 if(typeof(file.target) != 'undefined' && file.target){
29453 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29455 var previewEl = this.managerEl.createChild({
29457 cls : 'roo-document-manager-preview',
29461 tooltip : file[this.toolTipName],
29462 cls : 'roo-document-manager-thumb',
29463 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29468 html : '<i class="fa fa-times-circle"></i>'
29473 var close = previewEl.select('button.close', true).first();
29475 close.on('click', this.onRemove, this, file);
29477 file.target = previewEl;
29479 var image = previewEl.select('img', true).first();
29483 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29485 image.on('click', this.onClick, this, file);
29487 this.fireEvent('previewrendered', this, file);
29493 onPreviewLoad : function(file, image)
29495 if(typeof(file.target) == 'undefined' || !file.target){
29499 var width = image.dom.naturalWidth || image.dom.width;
29500 var height = image.dom.naturalHeight || image.dom.height;
29502 if(!this.previewResize) {
29506 if(width > height){
29507 file.target.addClass('wide');
29511 file.target.addClass('tall');
29516 uploadFromSource : function(file, crop)
29518 this.xhr = new XMLHttpRequest();
29520 this.managerEl.createChild({
29522 cls : 'roo-document-manager-loading',
29526 tooltip : file.name,
29527 cls : 'roo-document-manager-thumb',
29528 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29534 this.xhr.open(this.method, this.url, true);
29537 "Accept": "application/json",
29538 "Cache-Control": "no-cache",
29539 "X-Requested-With": "XMLHttpRequest"
29542 for (var headerName in headers) {
29543 var headerValue = headers[headerName];
29545 this.xhr.setRequestHeader(headerName, headerValue);
29551 this.xhr.onload = function()
29553 _this.xhrOnLoad(_this.xhr);
29556 this.xhr.onerror = function()
29558 _this.xhrOnError(_this.xhr);
29561 var formData = new FormData();
29563 formData.append('returnHTML', 'NO');
29565 formData.append('crop', crop);
29567 if(typeof(file.filename) != 'undefined'){
29568 formData.append('filename', file.filename);
29571 if(typeof(file.mimetype) != 'undefined'){
29572 formData.append('mimetype', file.mimetype);
29577 if(this.fireEvent('prepare', this, formData) != false){
29578 this.xhr.send(formData);
29588 * @class Roo.bootstrap.DocumentViewer
29589 * @extends Roo.bootstrap.Component
29590 * Bootstrap DocumentViewer class
29591 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29592 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29595 * Create a new DocumentViewer
29596 * @param {Object} config The config object
29599 Roo.bootstrap.DocumentViewer = function(config){
29600 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29605 * Fire after initEvent
29606 * @param {Roo.bootstrap.DocumentViewer} this
29612 * @param {Roo.bootstrap.DocumentViewer} this
29617 * Fire after download button
29618 * @param {Roo.bootstrap.DocumentViewer} this
29623 * Fire after trash button
29624 * @param {Roo.bootstrap.DocumentViewer} this
29631 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29633 showDownload : true,
29637 getAutoCreate : function()
29641 cls : 'roo-document-viewer',
29645 cls : 'roo-document-viewer-body',
29649 cls : 'roo-document-viewer-thumb',
29653 cls : 'roo-document-viewer-image'
29661 cls : 'roo-document-viewer-footer',
29664 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29668 cls : 'btn-group roo-document-viewer-download',
29672 cls : 'btn btn-default',
29673 html : '<i class="fa fa-download"></i>'
29679 cls : 'btn-group roo-document-viewer-trash',
29683 cls : 'btn btn-default',
29684 html : '<i class="fa fa-trash"></i>'
29697 initEvents : function()
29699 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29700 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29702 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29703 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29705 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29706 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29708 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29709 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29711 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29712 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29714 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29715 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29717 this.bodyEl.on('click', this.onClick, this);
29718 this.downloadBtn.on('click', this.onDownload, this);
29719 this.trashBtn.on('click', this.onTrash, this);
29721 this.downloadBtn.hide();
29722 this.trashBtn.hide();
29724 if(this.showDownload){
29725 this.downloadBtn.show();
29728 if(this.showTrash){
29729 this.trashBtn.show();
29732 if(!this.showDownload && !this.showTrash) {
29733 this.footerEl.hide();
29738 initial : function()
29740 this.fireEvent('initial', this);
29744 onClick : function(e)
29746 e.preventDefault();
29748 this.fireEvent('click', this);
29751 onDownload : function(e)
29753 e.preventDefault();
29755 this.fireEvent('download', this);
29758 onTrash : function(e)
29760 e.preventDefault();
29762 this.fireEvent('trash', this);
29774 * @class Roo.bootstrap.NavProgressBar
29775 * @extends Roo.bootstrap.Component
29776 * Bootstrap NavProgressBar class
29779 * Create a new nav progress bar
29780 * @param {Object} config The config object
29783 Roo.bootstrap.NavProgressBar = function(config){
29784 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29786 this.bullets = this.bullets || [];
29788 // Roo.bootstrap.NavProgressBar.register(this);
29792 * Fires when the active item changes
29793 * @param {Roo.bootstrap.NavProgressBar} this
29794 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29795 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29802 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29807 getAutoCreate : function()
29809 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29813 cls : 'roo-navigation-bar-group',
29817 cls : 'roo-navigation-top-bar'
29821 cls : 'roo-navigation-bullets-bar',
29825 cls : 'roo-navigation-bar'
29832 cls : 'roo-navigation-bottom-bar'
29842 initEvents: function()
29847 onRender : function(ct, position)
29849 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29851 if(this.bullets.length){
29852 Roo.each(this.bullets, function(b){
29861 addItem : function(cfg)
29863 var item = new Roo.bootstrap.NavProgressItem(cfg);
29865 item.parentId = this.id;
29866 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29869 var top = new Roo.bootstrap.Element({
29871 cls : 'roo-navigation-bar-text'
29874 var bottom = new Roo.bootstrap.Element({
29876 cls : 'roo-navigation-bar-text'
29879 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29880 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29882 var topText = new Roo.bootstrap.Element({
29884 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29887 var bottomText = new Roo.bootstrap.Element({
29889 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29892 topText.onRender(top.el, null);
29893 bottomText.onRender(bottom.el, null);
29896 item.bottomEl = bottom;
29899 this.barItems.push(item);
29904 getActive : function()
29906 var active = false;
29908 Roo.each(this.barItems, function(v){
29910 if (!v.isActive()) {
29922 setActiveItem : function(item)
29926 Roo.each(this.barItems, function(v){
29927 if (v.rid == item.rid) {
29931 if (v.isActive()) {
29932 v.setActive(false);
29937 item.setActive(true);
29939 this.fireEvent('changed', this, item, prev);
29942 getBarItem: function(rid)
29946 Roo.each(this.barItems, function(e) {
29947 if (e.rid != rid) {
29958 indexOfItem : function(item)
29962 Roo.each(this.barItems, function(v, i){
29964 if (v.rid != item.rid) {
29975 setActiveNext : function()
29977 var i = this.indexOfItem(this.getActive());
29979 if (i > this.barItems.length) {
29983 this.setActiveItem(this.barItems[i+1]);
29986 setActivePrev : function()
29988 var i = this.indexOfItem(this.getActive());
29994 this.setActiveItem(this.barItems[i-1]);
29997 format : function()
29999 if(!this.barItems.length){
30003 var width = 100 / this.barItems.length;
30005 Roo.each(this.barItems, function(i){
30006 i.el.setStyle('width', width + '%');
30007 i.topEl.el.setStyle('width', width + '%');
30008 i.bottomEl.el.setStyle('width', width + '%');
30017 * Nav Progress Item
30022 * @class Roo.bootstrap.NavProgressItem
30023 * @extends Roo.bootstrap.Component
30024 * Bootstrap NavProgressItem class
30025 * @cfg {String} rid the reference id
30026 * @cfg {Boolean} active (true|false) Is item active default false
30027 * @cfg {Boolean} disabled (true|false) Is item active default false
30028 * @cfg {String} html
30029 * @cfg {String} position (top|bottom) text position default bottom
30030 * @cfg {String} icon show icon instead of number
30033 * Create a new NavProgressItem
30034 * @param {Object} config The config object
30036 Roo.bootstrap.NavProgressItem = function(config){
30037 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30042 * The raw click event for the entire grid.
30043 * @param {Roo.bootstrap.NavProgressItem} this
30044 * @param {Roo.EventObject} e
30051 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30057 position : 'bottom',
30060 getAutoCreate : function()
30062 var iconCls = 'roo-navigation-bar-item-icon';
30064 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30068 cls: 'roo-navigation-bar-item',
30078 cfg.cls += ' active';
30081 cfg.cls += ' disabled';
30087 disable : function()
30089 this.setDisabled(true);
30092 enable : function()
30094 this.setDisabled(false);
30097 initEvents: function()
30099 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30101 this.iconEl.on('click', this.onClick, this);
30104 onClick : function(e)
30106 e.preventDefault();
30112 if(this.fireEvent('click', this, e) === false){
30116 this.parent().setActiveItem(this);
30119 isActive: function ()
30121 return this.active;
30124 setActive : function(state)
30126 if(this.active == state){
30130 this.active = state;
30133 this.el.addClass('active');
30137 this.el.removeClass('active');
30142 setDisabled : function(state)
30144 if(this.disabled == state){
30148 this.disabled = state;
30151 this.el.addClass('disabled');
30155 this.el.removeClass('disabled');
30158 tooltipEl : function()
30160 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30173 * @class Roo.bootstrap.FieldLabel
30174 * @extends Roo.bootstrap.Component
30175 * Bootstrap FieldLabel class
30176 * @cfg {String} html contents of the element
30177 * @cfg {String} tag tag of the element default label
30178 * @cfg {String} cls class of the element
30179 * @cfg {String} target label target
30180 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30181 * @cfg {String} invalidClass default "text-warning"
30182 * @cfg {String} validClass default "text-success"
30183 * @cfg {String} iconTooltip default "This field is required"
30184 * @cfg {String} indicatorpos (left|right) default left
30187 * Create a new FieldLabel
30188 * @param {Object} config The config object
30191 Roo.bootstrap.FieldLabel = function(config){
30192 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30197 * Fires after the field has been marked as invalid.
30198 * @param {Roo.form.FieldLabel} this
30199 * @param {String} msg The validation message
30204 * Fires after the field has been validated with no errors.
30205 * @param {Roo.form.FieldLabel} this
30211 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30218 invalidClass : 'has-warning',
30219 validClass : 'has-success',
30220 iconTooltip : 'This field is required',
30221 indicatorpos : 'left',
30223 getAutoCreate : function(){
30226 if (!this.allowBlank) {
30232 cls : 'roo-bootstrap-field-label ' + this.cls,
30237 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30238 tooltip : this.iconTooltip
30247 if(this.indicatorpos == 'right'){
30250 cls : 'roo-bootstrap-field-label ' + this.cls,
30259 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30260 tooltip : this.iconTooltip
30269 initEvents: function()
30271 Roo.bootstrap.Element.superclass.initEvents.call(this);
30273 this.indicator = this.indicatorEl();
30275 if(this.indicator){
30276 this.indicator.removeClass('visible');
30277 this.indicator.addClass('invisible');
30280 Roo.bootstrap.FieldLabel.register(this);
30283 indicatorEl : function()
30285 var indicator = this.el.select('i.roo-required-indicator',true).first();
30296 * Mark this field as valid
30298 markValid : function()
30300 if(this.indicator){
30301 this.indicator.removeClass('visible');
30302 this.indicator.addClass('invisible');
30305 this.el.removeClass(this.invalidClass);
30307 this.el.addClass(this.validClass);
30309 this.fireEvent('valid', this);
30313 * Mark this field as invalid
30314 * @param {String} msg The validation message
30316 markInvalid : function(msg)
30318 if(this.indicator){
30319 this.indicator.removeClass('invisible');
30320 this.indicator.addClass('visible');
30323 this.el.removeClass(this.validClass);
30325 this.el.addClass(this.invalidClass);
30327 this.fireEvent('invalid', this, msg);
30333 Roo.apply(Roo.bootstrap.FieldLabel, {
30338 * register a FieldLabel Group
30339 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30341 register : function(label)
30343 if(this.groups.hasOwnProperty(label.target)){
30347 this.groups[label.target] = label;
30351 * fetch a FieldLabel Group based on the target
30352 * @param {string} target
30353 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30355 get: function(target) {
30356 if (typeof(this.groups[target]) == 'undefined') {
30360 return this.groups[target] ;
30369 * page DateSplitField.
30375 * @class Roo.bootstrap.DateSplitField
30376 * @extends Roo.bootstrap.Component
30377 * Bootstrap DateSplitField class
30378 * @cfg {string} fieldLabel - the label associated
30379 * @cfg {Number} labelWidth set the width of label (0-12)
30380 * @cfg {String} labelAlign (top|left)
30381 * @cfg {Boolean} dayAllowBlank (true|false) default false
30382 * @cfg {Boolean} monthAllowBlank (true|false) default false
30383 * @cfg {Boolean} yearAllowBlank (true|false) default false
30384 * @cfg {string} dayPlaceholder
30385 * @cfg {string} monthPlaceholder
30386 * @cfg {string} yearPlaceholder
30387 * @cfg {string} dayFormat default 'd'
30388 * @cfg {string} monthFormat default 'm'
30389 * @cfg {string} yearFormat default 'Y'
30390 * @cfg {Number} labellg set the width of label (1-12)
30391 * @cfg {Number} labelmd set the width of label (1-12)
30392 * @cfg {Number} labelsm set the width of label (1-12)
30393 * @cfg {Number} labelxs set the width of label (1-12)
30397 * Create a new DateSplitField
30398 * @param {Object} config The config object
30401 Roo.bootstrap.DateSplitField = function(config){
30402 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30408 * getting the data of years
30409 * @param {Roo.bootstrap.DateSplitField} this
30410 * @param {Object} years
30415 * getting the data of days
30416 * @param {Roo.bootstrap.DateSplitField} this
30417 * @param {Object} days
30422 * Fires after the field has been marked as invalid.
30423 * @param {Roo.form.Field} this
30424 * @param {String} msg The validation message
30429 * Fires after the field has been validated with no errors.
30430 * @param {Roo.form.Field} this
30436 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30439 labelAlign : 'top',
30441 dayAllowBlank : false,
30442 monthAllowBlank : false,
30443 yearAllowBlank : false,
30444 dayPlaceholder : '',
30445 monthPlaceholder : '',
30446 yearPlaceholder : '',
30450 isFormField : true,
30456 getAutoCreate : function()
30460 cls : 'row roo-date-split-field-group',
30465 cls : 'form-hidden-field roo-date-split-field-group-value',
30471 var labelCls = 'col-md-12';
30472 var contentCls = 'col-md-4';
30474 if(this.fieldLabel){
30478 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30482 html : this.fieldLabel
30487 if(this.labelAlign == 'left'){
30489 if(this.labelWidth > 12){
30490 label.style = "width: " + this.labelWidth + 'px';
30493 if(this.labelWidth < 13 && this.labelmd == 0){
30494 this.labelmd = this.labelWidth;
30497 if(this.labellg > 0){
30498 labelCls = ' col-lg-' + this.labellg;
30499 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30502 if(this.labelmd > 0){
30503 labelCls = ' col-md-' + this.labelmd;
30504 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30507 if(this.labelsm > 0){
30508 labelCls = ' col-sm-' + this.labelsm;
30509 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30512 if(this.labelxs > 0){
30513 labelCls = ' col-xs-' + this.labelxs;
30514 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30518 label.cls += ' ' + labelCls;
30520 cfg.cn.push(label);
30523 Roo.each(['day', 'month', 'year'], function(t){
30526 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30533 inputEl: function ()
30535 return this.el.select('.roo-date-split-field-group-value', true).first();
30538 onRender : function(ct, position)
30542 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30544 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30546 this.dayField = new Roo.bootstrap.ComboBox({
30547 allowBlank : this.dayAllowBlank,
30548 alwaysQuery : true,
30549 displayField : 'value',
30552 forceSelection : true,
30554 placeholder : this.dayPlaceholder,
30555 selectOnFocus : true,
30556 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30557 triggerAction : 'all',
30559 valueField : 'value',
30560 store : new Roo.data.SimpleStore({
30561 data : (function() {
30563 _this.fireEvent('days', _this, days);
30566 fields : [ 'value' ]
30569 select : function (_self, record, index)
30571 _this.setValue(_this.getValue());
30576 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30578 this.monthField = new Roo.bootstrap.MonthField({
30579 after : '<i class=\"fa fa-calendar\"></i>',
30580 allowBlank : this.monthAllowBlank,
30581 placeholder : this.monthPlaceholder,
30584 render : function (_self)
30586 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30587 e.preventDefault();
30591 select : function (_self, oldvalue, newvalue)
30593 _this.setValue(_this.getValue());
30598 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30600 this.yearField = new Roo.bootstrap.ComboBox({
30601 allowBlank : this.yearAllowBlank,
30602 alwaysQuery : true,
30603 displayField : 'value',
30606 forceSelection : true,
30608 placeholder : this.yearPlaceholder,
30609 selectOnFocus : true,
30610 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30611 triggerAction : 'all',
30613 valueField : 'value',
30614 store : new Roo.data.SimpleStore({
30615 data : (function() {
30617 _this.fireEvent('years', _this, years);
30620 fields : [ 'value' ]
30623 select : function (_self, record, index)
30625 _this.setValue(_this.getValue());
30630 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30633 setValue : function(v, format)
30635 this.inputEl.dom.value = v;
30637 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30639 var d = Date.parseDate(v, f);
30646 this.setDay(d.format(this.dayFormat));
30647 this.setMonth(d.format(this.monthFormat));
30648 this.setYear(d.format(this.yearFormat));
30655 setDay : function(v)
30657 this.dayField.setValue(v);
30658 this.inputEl.dom.value = this.getValue();
30663 setMonth : function(v)
30665 this.monthField.setValue(v, true);
30666 this.inputEl.dom.value = this.getValue();
30671 setYear : function(v)
30673 this.yearField.setValue(v);
30674 this.inputEl.dom.value = this.getValue();
30679 getDay : function()
30681 return this.dayField.getValue();
30684 getMonth : function()
30686 return this.monthField.getValue();
30689 getYear : function()
30691 return this.yearField.getValue();
30694 getValue : function()
30696 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30698 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30708 this.inputEl.dom.value = '';
30713 validate : function()
30715 var d = this.dayField.validate();
30716 var m = this.monthField.validate();
30717 var y = this.yearField.validate();
30722 (!this.dayAllowBlank && !d) ||
30723 (!this.monthAllowBlank && !m) ||
30724 (!this.yearAllowBlank && !y)
30729 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30738 this.markInvalid();
30743 markValid : function()
30746 var label = this.el.select('label', true).first();
30747 var icon = this.el.select('i.fa-star', true).first();
30753 this.fireEvent('valid', this);
30757 * Mark this field as invalid
30758 * @param {String} msg The validation message
30760 markInvalid : function(msg)
30763 var label = this.el.select('label', true).first();
30764 var icon = this.el.select('i.fa-star', true).first();
30766 if(label && !icon){
30767 this.el.select('.roo-date-split-field-label', true).createChild({
30769 cls : 'text-danger fa fa-lg fa-star',
30770 tooltip : 'This field is required',
30771 style : 'margin-right:5px;'
30775 this.fireEvent('invalid', this, msg);
30778 clearInvalid : function()
30780 var label = this.el.select('label', true).first();
30781 var icon = this.el.select('i.fa-star', true).first();
30787 this.fireEvent('valid', this);
30790 getName: function()
30800 * http://masonry.desandro.com
30802 * The idea is to render all the bricks based on vertical width...
30804 * The original code extends 'outlayer' - we might need to use that....
30810 * @class Roo.bootstrap.LayoutMasonry
30811 * @extends Roo.bootstrap.Component
30812 * Bootstrap Layout Masonry class
30815 * Create a new Element
30816 * @param {Object} config The config object
30819 Roo.bootstrap.LayoutMasonry = function(config){
30821 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30825 Roo.bootstrap.LayoutMasonry.register(this);
30831 * Fire after layout the items
30832 * @param {Roo.bootstrap.LayoutMasonry} this
30833 * @param {Roo.EventObject} e
30840 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30843 * @cfg {Boolean} isLayoutInstant = no animation?
30845 isLayoutInstant : false, // needed?
30848 * @cfg {Number} boxWidth width of the columns
30853 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30858 * @cfg {Number} padWidth padding below box..
30863 * @cfg {Number} gutter gutter width..
30868 * @cfg {Number} maxCols maximum number of columns
30874 * @cfg {Boolean} isAutoInitial defalut true
30876 isAutoInitial : true,
30881 * @cfg {Boolean} isHorizontal defalut false
30883 isHorizontal : false,
30885 currentSize : null,
30891 bricks: null, //CompositeElement
30895 _isLayoutInited : false,
30897 // isAlternative : false, // only use for vertical layout...
30900 * @cfg {Number} alternativePadWidth padding below box..
30902 alternativePadWidth : 50,
30904 selectedBrick : [],
30906 getAutoCreate : function(){
30908 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30912 cls: 'blog-masonary-wrapper ' + this.cls,
30914 cls : 'mas-boxes masonary'
30921 getChildContainer: function( )
30923 if (this.boxesEl) {
30924 return this.boxesEl;
30927 this.boxesEl = this.el.select('.mas-boxes').first();
30929 return this.boxesEl;
30933 initEvents : function()
30937 if(this.isAutoInitial){
30938 Roo.log('hook children rendered');
30939 this.on('childrenrendered', function() {
30940 Roo.log('children rendered');
30946 initial : function()
30948 this.selectedBrick = [];
30950 this.currentSize = this.el.getBox(true);
30952 Roo.EventManager.onWindowResize(this.resize, this);
30954 if(!this.isAutoInitial){
30962 //this.layout.defer(500,this);
30966 resize : function()
30968 var cs = this.el.getBox(true);
30971 this.currentSize.width == cs.width &&
30972 this.currentSize.x == cs.x &&
30973 this.currentSize.height == cs.height &&
30974 this.currentSize.y == cs.y
30976 Roo.log("no change in with or X or Y");
30980 this.currentSize = cs;
30986 layout : function()
30988 this._resetLayout();
30990 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30992 this.layoutItems( isInstant );
30994 this._isLayoutInited = true;
30996 this.fireEvent('layout', this);
31000 _resetLayout : function()
31002 if(this.isHorizontal){
31003 this.horizontalMeasureColumns();
31007 this.verticalMeasureColumns();
31011 verticalMeasureColumns : function()
31013 this.getContainerWidth();
31015 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31016 // this.colWidth = Math.floor(this.containerWidth * 0.8);
31020 var boxWidth = this.boxWidth + this.padWidth;
31022 if(this.containerWidth < this.boxWidth){
31023 boxWidth = this.containerWidth
31026 var containerWidth = this.containerWidth;
31028 var cols = Math.floor(containerWidth / boxWidth);
31030 this.cols = Math.max( cols, 1 );
31032 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31034 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31036 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31038 this.colWidth = boxWidth + avail - this.padWidth;
31040 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31041 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31044 horizontalMeasureColumns : function()
31046 this.getContainerWidth();
31048 var boxWidth = this.boxWidth;
31050 if(this.containerWidth < boxWidth){
31051 boxWidth = this.containerWidth;
31054 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31056 this.el.setHeight(boxWidth);
31060 getContainerWidth : function()
31062 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31065 layoutItems : function( isInstant )
31067 Roo.log(this.bricks);
31069 var items = Roo.apply([], this.bricks);
31071 if(this.isHorizontal){
31072 this._horizontalLayoutItems( items , isInstant );
31076 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31077 // this._verticalAlternativeLayoutItems( items , isInstant );
31081 this._verticalLayoutItems( items , isInstant );
31085 _verticalLayoutItems : function ( items , isInstant)
31087 if ( !items || !items.length ) {
31092 ['xs', 'xs', 'xs', 'tall'],
31093 ['xs', 'xs', 'tall'],
31094 ['xs', 'xs', 'sm'],
31095 ['xs', 'xs', 'xs'],
31101 ['sm', 'xs', 'xs'],
31105 ['tall', 'xs', 'xs', 'xs'],
31106 ['tall', 'xs', 'xs'],
31118 Roo.each(items, function(item, k){
31120 switch (item.size) {
31121 // these layouts take up a full box,
31132 boxes.push([item]);
31155 var filterPattern = function(box, length)
31163 var pattern = box.slice(0, length);
31167 Roo.each(pattern, function(i){
31168 format.push(i.size);
31171 Roo.each(standard, function(s){
31173 if(String(s) != String(format)){
31182 if(!match && length == 1){
31187 filterPattern(box, length - 1);
31191 queue.push(pattern);
31193 box = box.slice(length, box.length);
31195 filterPattern(box, 4);
31201 Roo.each(boxes, function(box, k){
31207 if(box.length == 1){
31212 filterPattern(box, 4);
31216 this._processVerticalLayoutQueue( queue, isInstant );
31220 // _verticalAlternativeLayoutItems : function( items , isInstant )
31222 // if ( !items || !items.length ) {
31226 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31230 _horizontalLayoutItems : function ( items , isInstant)
31232 if ( !items || !items.length || items.length < 3) {
31238 var eItems = items.slice(0, 3);
31240 items = items.slice(3, items.length);
31243 ['xs', 'xs', 'xs', 'wide'],
31244 ['xs', 'xs', 'wide'],
31245 ['xs', 'xs', 'sm'],
31246 ['xs', 'xs', 'xs'],
31252 ['sm', 'xs', 'xs'],
31256 ['wide', 'xs', 'xs', 'xs'],
31257 ['wide', 'xs', 'xs'],
31270 Roo.each(items, function(item, k){
31272 switch (item.size) {
31283 boxes.push([item]);
31307 var filterPattern = function(box, length)
31315 var pattern = box.slice(0, length);
31319 Roo.each(pattern, function(i){
31320 format.push(i.size);
31323 Roo.each(standard, function(s){
31325 if(String(s) != String(format)){
31334 if(!match && length == 1){
31339 filterPattern(box, length - 1);
31343 queue.push(pattern);
31345 box = box.slice(length, box.length);
31347 filterPattern(box, 4);
31353 Roo.each(boxes, function(box, k){
31359 if(box.length == 1){
31364 filterPattern(box, 4);
31371 var pos = this.el.getBox(true);
31375 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31377 var hit_end = false;
31379 Roo.each(queue, function(box){
31383 Roo.each(box, function(b){
31385 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31395 Roo.each(box, function(b){
31397 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31400 mx = Math.max(mx, b.x);
31404 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31408 Roo.each(box, function(b){
31410 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31424 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31427 /** Sets position of item in DOM
31428 * @param {Element} item
31429 * @param {Number} x - horizontal position
31430 * @param {Number} y - vertical position
31431 * @param {Boolean} isInstant - disables transitions
31433 _processVerticalLayoutQueue : function( queue, isInstant )
31435 var pos = this.el.getBox(true);
31440 for (var i = 0; i < this.cols; i++){
31444 Roo.each(queue, function(box, k){
31446 var col = k % this.cols;
31448 Roo.each(box, function(b,kk){
31450 b.el.position('absolute');
31452 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31453 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31455 if(b.size == 'md-left' || b.size == 'md-right'){
31456 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31457 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31460 b.el.setWidth(width);
31461 b.el.setHeight(height);
31463 b.el.select('iframe',true).setSize(width,height);
31467 for (var i = 0; i < this.cols; i++){
31469 if(maxY[i] < maxY[col]){
31474 col = Math.min(col, i);
31478 x = pos.x + col * (this.colWidth + this.padWidth);
31482 var positions = [];
31484 switch (box.length){
31486 positions = this.getVerticalOneBoxColPositions(x, y, box);
31489 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31492 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31495 positions = this.getVerticalFourBoxColPositions(x, y, box);
31501 Roo.each(box, function(b,kk){
31503 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31505 var sz = b.el.getSize();
31507 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31515 for (var i = 0; i < this.cols; i++){
31516 mY = Math.max(mY, maxY[i]);
31519 this.el.setHeight(mY - pos.y);
31523 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31525 // var pos = this.el.getBox(true);
31528 // var maxX = pos.right;
31530 // var maxHeight = 0;
31532 // Roo.each(items, function(item, k){
31536 // item.el.position('absolute');
31538 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31540 // item.el.setWidth(width);
31542 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31544 // item.el.setHeight(height);
31547 // item.el.setXY([x, y], isInstant ? false : true);
31549 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31552 // y = y + height + this.alternativePadWidth;
31554 // maxHeight = maxHeight + height + this.alternativePadWidth;
31558 // this.el.setHeight(maxHeight);
31562 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31564 var pos = this.el.getBox(true);
31569 var maxX = pos.right;
31571 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31573 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31575 Roo.each(queue, function(box, k){
31577 Roo.each(box, function(b, kk){
31579 b.el.position('absolute');
31581 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31582 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31584 if(b.size == 'md-left' || b.size == 'md-right'){
31585 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31586 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31589 b.el.setWidth(width);
31590 b.el.setHeight(height);
31598 var positions = [];
31600 switch (box.length){
31602 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31605 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31608 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31611 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31617 Roo.each(box, function(b,kk){
31619 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31621 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31629 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31631 Roo.each(eItems, function(b,k){
31633 b.size = (k == 0) ? 'sm' : 'xs';
31634 b.x = (k == 0) ? 2 : 1;
31635 b.y = (k == 0) ? 2 : 1;
31637 b.el.position('absolute');
31639 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31641 b.el.setWidth(width);
31643 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31645 b.el.setHeight(height);
31649 var positions = [];
31652 x : maxX - this.unitWidth * 2 - this.gutter,
31657 x : maxX - this.unitWidth,
31658 y : minY + (this.unitWidth + this.gutter) * 2
31662 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31666 Roo.each(eItems, function(b,k){
31668 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31674 getVerticalOneBoxColPositions : function(x, y, box)
31678 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31680 if(box[0].size == 'md-left'){
31684 if(box[0].size == 'md-right'){
31689 x : x + (this.unitWidth + this.gutter) * rand,
31696 getVerticalTwoBoxColPositions : function(x, y, box)
31700 if(box[0].size == 'xs'){
31704 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31708 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31722 x : x + (this.unitWidth + this.gutter) * 2,
31723 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31730 getVerticalThreeBoxColPositions : function(x, y, box)
31734 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31742 x : x + (this.unitWidth + this.gutter) * 1,
31747 x : x + (this.unitWidth + this.gutter) * 2,
31755 if(box[0].size == 'xs' && box[1].size == 'xs'){
31764 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31768 x : x + (this.unitWidth + this.gutter) * 1,
31782 x : x + (this.unitWidth + this.gutter) * 2,
31787 x : x + (this.unitWidth + this.gutter) * 2,
31788 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31795 getVerticalFourBoxColPositions : function(x, y, box)
31799 if(box[0].size == 'xs'){
31808 y : y + (this.unitHeight + this.gutter) * 1
31813 y : y + (this.unitHeight + this.gutter) * 2
31817 x : x + (this.unitWidth + this.gutter) * 1,
31831 x : x + (this.unitWidth + this.gutter) * 2,
31836 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31837 y : y + (this.unitHeight + this.gutter) * 1
31841 x : x + (this.unitWidth + this.gutter) * 2,
31842 y : y + (this.unitWidth + this.gutter) * 2
31849 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31853 if(box[0].size == 'md-left'){
31855 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31862 if(box[0].size == 'md-right'){
31864 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31865 y : minY + (this.unitWidth + this.gutter) * 1
31871 var rand = Math.floor(Math.random() * (4 - box[0].y));
31874 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31875 y : minY + (this.unitWidth + this.gutter) * rand
31882 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31886 if(box[0].size == 'xs'){
31889 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31894 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31895 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31903 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31908 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31909 y : minY + (this.unitWidth + this.gutter) * 2
31916 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31920 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31923 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31928 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31929 y : minY + (this.unitWidth + this.gutter) * 1
31933 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31934 y : minY + (this.unitWidth + this.gutter) * 2
31941 if(box[0].size == 'xs' && box[1].size == 'xs'){
31944 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31949 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31954 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31955 y : minY + (this.unitWidth + this.gutter) * 1
31963 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31968 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31969 y : minY + (this.unitWidth + this.gutter) * 2
31973 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31974 y : minY + (this.unitWidth + this.gutter) * 2
31981 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31985 if(box[0].size == 'xs'){
31988 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31993 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31998 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),
32003 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32004 y : minY + (this.unitWidth + this.gutter) * 1
32012 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32017 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32018 y : minY + (this.unitWidth + this.gutter) * 2
32022 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32023 y : minY + (this.unitWidth + this.gutter) * 2
32027 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),
32028 y : minY + (this.unitWidth + this.gutter) * 2
32036 * remove a Masonry Brick
32037 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32039 removeBrick : function(brick_id)
32045 for (var i = 0; i<this.bricks.length; i++) {
32046 if (this.bricks[i].id == brick_id) {
32047 this.bricks.splice(i,1);
32048 this.el.dom.removeChild(Roo.get(brick_id).dom);
32055 * adds a Masonry Brick
32056 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32058 addBrick : function(cfg)
32060 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32061 //this.register(cn);
32062 cn.parentId = this.id;
32063 cn.render(this.el);
32068 * register a Masonry Brick
32069 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32072 register : function(brick)
32074 this.bricks.push(brick);
32075 brick.masonryId = this.id;
32079 * clear all the Masonry Brick
32081 clearAll : function()
32084 //this.getChildContainer().dom.innerHTML = "";
32085 this.el.dom.innerHTML = '';
32088 getSelected : function()
32090 if (!this.selectedBrick) {
32094 return this.selectedBrick;
32098 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32102 * register a Masonry Layout
32103 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32106 register : function(layout)
32108 this.groups[layout.id] = layout;
32111 * fetch a Masonry Layout based on the masonry layout ID
32112 * @param {string} the masonry layout to add
32113 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32116 get: function(layout_id) {
32117 if (typeof(this.groups[layout_id]) == 'undefined') {
32120 return this.groups[layout_id] ;
32132 * http://masonry.desandro.com
32134 * The idea is to render all the bricks based on vertical width...
32136 * The original code extends 'outlayer' - we might need to use that....
32142 * @class Roo.bootstrap.LayoutMasonryAuto
32143 * @extends Roo.bootstrap.Component
32144 * Bootstrap Layout Masonry class
32147 * Create a new Element
32148 * @param {Object} config The config object
32151 Roo.bootstrap.LayoutMasonryAuto = function(config){
32152 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32155 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32158 * @cfg {Boolean} isFitWidth - resize the width..
32160 isFitWidth : false, // options..
32162 * @cfg {Boolean} isOriginLeft = left align?
32164 isOriginLeft : true,
32166 * @cfg {Boolean} isOriginTop = top align?
32168 isOriginTop : false,
32170 * @cfg {Boolean} isLayoutInstant = no animation?
32172 isLayoutInstant : false, // needed?
32174 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32176 isResizingContainer : true,
32178 * @cfg {Number} columnWidth width of the columns
32184 * @cfg {Number} maxCols maximum number of columns
32189 * @cfg {Number} padHeight padding below box..
32195 * @cfg {Boolean} isAutoInitial defalut true
32198 isAutoInitial : true,
32204 initialColumnWidth : 0,
32205 currentSize : null,
32207 colYs : null, // array.
32214 bricks: null, //CompositeElement
32215 cols : 0, // array?
32216 // element : null, // wrapped now this.el
32217 _isLayoutInited : null,
32220 getAutoCreate : function(){
32224 cls: 'blog-masonary-wrapper ' + this.cls,
32226 cls : 'mas-boxes masonary'
32233 getChildContainer: function( )
32235 if (this.boxesEl) {
32236 return this.boxesEl;
32239 this.boxesEl = this.el.select('.mas-boxes').first();
32241 return this.boxesEl;
32245 initEvents : function()
32249 if(this.isAutoInitial){
32250 Roo.log('hook children rendered');
32251 this.on('childrenrendered', function() {
32252 Roo.log('children rendered');
32259 initial : function()
32261 this.reloadItems();
32263 this.currentSize = this.el.getBox(true);
32265 /// was window resize... - let's see if this works..
32266 Roo.EventManager.onWindowResize(this.resize, this);
32268 if(!this.isAutoInitial){
32273 this.layout.defer(500,this);
32276 reloadItems: function()
32278 this.bricks = this.el.select('.masonry-brick', true);
32280 this.bricks.each(function(b) {
32281 //Roo.log(b.getSize());
32282 if (!b.attr('originalwidth')) {
32283 b.attr('originalwidth', b.getSize().width);
32288 Roo.log(this.bricks.elements.length);
32291 resize : function()
32294 var cs = this.el.getBox(true);
32296 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32297 Roo.log("no change in with or X");
32300 this.currentSize = cs;
32304 layout : function()
32307 this._resetLayout();
32308 //this._manageStamps();
32310 // don't animate first layout
32311 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32312 this.layoutItems( isInstant );
32314 // flag for initalized
32315 this._isLayoutInited = true;
32318 layoutItems : function( isInstant )
32320 //var items = this._getItemsForLayout( this.items );
32321 // original code supports filtering layout items.. we just ignore it..
32323 this._layoutItems( this.bricks , isInstant );
32325 this._postLayout();
32327 _layoutItems : function ( items , isInstant)
32329 //this.fireEvent( 'layout', this, items );
32332 if ( !items || !items.elements.length ) {
32333 // no items, emit event with empty array
32338 items.each(function(item) {
32339 Roo.log("layout item");
32341 // get x/y object from method
32342 var position = this._getItemLayoutPosition( item );
32344 position.item = item;
32345 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32346 queue.push( position );
32349 this._processLayoutQueue( queue );
32351 /** Sets position of item in DOM
32352 * @param {Element} item
32353 * @param {Number} x - horizontal position
32354 * @param {Number} y - vertical position
32355 * @param {Boolean} isInstant - disables transitions
32357 _processLayoutQueue : function( queue )
32359 for ( var i=0, len = queue.length; i < len; i++ ) {
32360 var obj = queue[i];
32361 obj.item.position('absolute');
32362 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32368 * Any logic you want to do after each layout,
32369 * i.e. size the container
32371 _postLayout : function()
32373 this.resizeContainer();
32376 resizeContainer : function()
32378 if ( !this.isResizingContainer ) {
32381 var size = this._getContainerSize();
32383 this.el.setSize(size.width,size.height);
32384 this.boxesEl.setSize(size.width,size.height);
32390 _resetLayout : function()
32392 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32393 this.colWidth = this.el.getWidth();
32394 //this.gutter = this.el.getWidth();
32396 this.measureColumns();
32402 this.colYs.push( 0 );
32408 measureColumns : function()
32410 this.getContainerWidth();
32411 // if columnWidth is 0, default to outerWidth of first item
32412 if ( !this.columnWidth ) {
32413 var firstItem = this.bricks.first();
32414 Roo.log(firstItem);
32415 this.columnWidth = this.containerWidth;
32416 if (firstItem && firstItem.attr('originalwidth') ) {
32417 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32419 // columnWidth fall back to item of first element
32420 Roo.log("set column width?");
32421 this.initialColumnWidth = this.columnWidth ;
32423 // if first elem has no width, default to size of container
32428 if (this.initialColumnWidth) {
32429 this.columnWidth = this.initialColumnWidth;
32434 // column width is fixed at the top - however if container width get's smaller we should
32437 // this bit calcs how man columns..
32439 var columnWidth = this.columnWidth += this.gutter;
32441 // calculate columns
32442 var containerWidth = this.containerWidth + this.gutter;
32444 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32445 // fix rounding errors, typically with gutters
32446 var excess = columnWidth - containerWidth % columnWidth;
32449 // if overshoot is less than a pixel, round up, otherwise floor it
32450 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32451 cols = Math[ mathMethod ]( cols );
32452 this.cols = Math.max( cols, 1 );
32453 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32455 // padding positioning..
32456 var totalColWidth = this.cols * this.columnWidth;
32457 var padavail = this.containerWidth - totalColWidth;
32458 // so for 2 columns - we need 3 'pads'
32460 var padNeeded = (1+this.cols) * this.padWidth;
32462 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32464 this.columnWidth += padExtra
32465 //this.padWidth = Math.floor(padavail / ( this.cols));
32467 // adjust colum width so that padding is fixed??
32469 // we have 3 columns ... total = width * 3
32470 // we have X left over... that should be used by
32472 //if (this.expandC) {
32480 getContainerWidth : function()
32482 /* // container is parent if fit width
32483 var container = this.isFitWidth ? this.element.parentNode : this.element;
32484 // check that this.size and size are there
32485 // IE8 triggers resize on body size change, so they might not be
32487 var size = getSize( container ); //FIXME
32488 this.containerWidth = size && size.innerWidth; //FIXME
32491 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32495 _getItemLayoutPosition : function( item ) // what is item?
32497 // we resize the item to our columnWidth..
32499 item.setWidth(this.columnWidth);
32500 item.autoBoxAdjust = false;
32502 var sz = item.getSize();
32504 // how many columns does this brick span
32505 var remainder = this.containerWidth % this.columnWidth;
32507 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32508 // round if off by 1 pixel, otherwise use ceil
32509 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32510 colSpan = Math.min( colSpan, this.cols );
32512 // normally this should be '1' as we dont' currently allow multi width columns..
32514 var colGroup = this._getColGroup( colSpan );
32515 // get the minimum Y value from the columns
32516 var minimumY = Math.min.apply( Math, colGroup );
32517 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32519 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32521 // position the brick
32523 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32524 y: this.currentSize.y + minimumY + this.padHeight
32528 // apply setHeight to necessary columns
32529 var setHeight = minimumY + sz.height + this.padHeight;
32530 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32532 var setSpan = this.cols + 1 - colGroup.length;
32533 for ( var i = 0; i < setSpan; i++ ) {
32534 this.colYs[ shortColIndex + i ] = setHeight ;
32541 * @param {Number} colSpan - number of columns the element spans
32542 * @returns {Array} colGroup
32544 _getColGroup : function( colSpan )
32546 if ( colSpan < 2 ) {
32547 // if brick spans only one column, use all the column Ys
32552 // how many different places could this brick fit horizontally
32553 var groupCount = this.cols + 1 - colSpan;
32554 // for each group potential horizontal position
32555 for ( var i = 0; i < groupCount; i++ ) {
32556 // make an array of colY values for that one group
32557 var groupColYs = this.colYs.slice( i, i + colSpan );
32558 // and get the max value of the array
32559 colGroup[i] = Math.max.apply( Math, groupColYs );
32564 _manageStamp : function( stamp )
32566 var stampSize = stamp.getSize();
32567 var offset = stamp.getBox();
32568 // get the columns that this stamp affects
32569 var firstX = this.isOriginLeft ? offset.x : offset.right;
32570 var lastX = firstX + stampSize.width;
32571 var firstCol = Math.floor( firstX / this.columnWidth );
32572 firstCol = Math.max( 0, firstCol );
32574 var lastCol = Math.floor( lastX / this.columnWidth );
32575 // lastCol should not go over if multiple of columnWidth #425
32576 lastCol -= lastX % this.columnWidth ? 0 : 1;
32577 lastCol = Math.min( this.cols - 1, lastCol );
32579 // set colYs to bottom of the stamp
32580 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32583 for ( var i = firstCol; i <= lastCol; i++ ) {
32584 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32589 _getContainerSize : function()
32591 this.maxY = Math.max.apply( Math, this.colYs );
32596 if ( this.isFitWidth ) {
32597 size.width = this._getContainerFitWidth();
32603 _getContainerFitWidth : function()
32605 var unusedCols = 0;
32606 // count unused columns
32609 if ( this.colYs[i] !== 0 ) {
32614 // fit container to columns that have been used
32615 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32618 needsResizeLayout : function()
32620 var previousWidth = this.containerWidth;
32621 this.getContainerWidth();
32622 return previousWidth !== this.containerWidth;
32637 * @class Roo.bootstrap.MasonryBrick
32638 * @extends Roo.bootstrap.Component
32639 * Bootstrap MasonryBrick class
32642 * Create a new MasonryBrick
32643 * @param {Object} config The config object
32646 Roo.bootstrap.MasonryBrick = function(config){
32648 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32650 Roo.bootstrap.MasonryBrick.register(this);
32656 * When a MasonryBrick is clcik
32657 * @param {Roo.bootstrap.MasonryBrick} this
32658 * @param {Roo.EventObject} e
32664 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32667 * @cfg {String} title
32671 * @cfg {String} html
32675 * @cfg {String} bgimage
32679 * @cfg {String} videourl
32683 * @cfg {String} cls
32687 * @cfg {String} href
32691 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32696 * @cfg {String} placetitle (center|bottom)
32701 * @cfg {Boolean} isFitContainer defalut true
32703 isFitContainer : true,
32706 * @cfg {Boolean} preventDefault defalut false
32708 preventDefault : false,
32711 * @cfg {Boolean} inverse defalut false
32713 maskInverse : false,
32715 getAutoCreate : function()
32717 if(!this.isFitContainer){
32718 return this.getSplitAutoCreate();
32721 var cls = 'masonry-brick masonry-brick-full';
32723 if(this.href.length){
32724 cls += ' masonry-brick-link';
32727 if(this.bgimage.length){
32728 cls += ' masonry-brick-image';
32731 if(this.maskInverse){
32732 cls += ' mask-inverse';
32735 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32736 cls += ' enable-mask';
32740 cls += ' masonry-' + this.size + '-brick';
32743 if(this.placetitle.length){
32745 switch (this.placetitle) {
32747 cls += ' masonry-center-title';
32750 cls += ' masonry-bottom-title';
32757 if(!this.html.length && !this.bgimage.length){
32758 cls += ' masonry-center-title';
32761 if(!this.html.length && this.bgimage.length){
32762 cls += ' masonry-bottom-title';
32767 cls += ' ' + this.cls;
32771 tag: (this.href.length) ? 'a' : 'div',
32776 cls: 'masonry-brick-mask'
32780 cls: 'masonry-brick-paragraph',
32786 if(this.href.length){
32787 cfg.href = this.href;
32790 var cn = cfg.cn[1].cn;
32792 if(this.title.length){
32795 cls: 'masonry-brick-title',
32800 if(this.html.length){
32803 cls: 'masonry-brick-text',
32808 if (!this.title.length && !this.html.length) {
32809 cfg.cn[1].cls += ' hide';
32812 if(this.bgimage.length){
32815 cls: 'masonry-brick-image-view',
32820 if(this.videourl.length){
32821 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32822 // youtube support only?
32825 cls: 'masonry-brick-image-view',
32828 allowfullscreen : true
32836 getSplitAutoCreate : function()
32838 var cls = 'masonry-brick masonry-brick-split';
32840 if(this.href.length){
32841 cls += ' masonry-brick-link';
32844 if(this.bgimage.length){
32845 cls += ' masonry-brick-image';
32849 cls += ' masonry-' + this.size + '-brick';
32852 switch (this.placetitle) {
32854 cls += ' masonry-center-title';
32857 cls += ' masonry-bottom-title';
32860 if(!this.bgimage.length){
32861 cls += ' masonry-center-title';
32864 if(this.bgimage.length){
32865 cls += ' masonry-bottom-title';
32871 cls += ' ' + this.cls;
32875 tag: (this.href.length) ? 'a' : 'div',
32880 cls: 'masonry-brick-split-head',
32884 cls: 'masonry-brick-paragraph',
32891 cls: 'masonry-brick-split-body',
32897 if(this.href.length){
32898 cfg.href = this.href;
32901 if(this.title.length){
32902 cfg.cn[0].cn[0].cn.push({
32904 cls: 'masonry-brick-title',
32909 if(this.html.length){
32910 cfg.cn[1].cn.push({
32912 cls: 'masonry-brick-text',
32917 if(this.bgimage.length){
32918 cfg.cn[0].cn.push({
32920 cls: 'masonry-brick-image-view',
32925 if(this.videourl.length){
32926 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32927 // youtube support only?
32928 cfg.cn[0].cn.cn.push({
32930 cls: 'masonry-brick-image-view',
32933 allowfullscreen : true
32940 initEvents: function()
32942 switch (this.size) {
32975 this.el.on('touchstart', this.onTouchStart, this);
32976 this.el.on('touchmove', this.onTouchMove, this);
32977 this.el.on('touchend', this.onTouchEnd, this);
32978 this.el.on('contextmenu', this.onContextMenu, this);
32980 this.el.on('mouseenter' ,this.enter, this);
32981 this.el.on('mouseleave', this.leave, this);
32982 this.el.on('click', this.onClick, this);
32985 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32986 this.parent().bricks.push(this);
32991 onClick: function(e, el)
32993 var time = this.endTimer - this.startTimer;
32994 // Roo.log(e.preventDefault());
32997 e.preventDefault();
33002 if(!this.preventDefault){
33006 e.preventDefault();
33008 if (this.activeClass != '') {
33009 this.selectBrick();
33012 this.fireEvent('click', this, e);
33015 enter: function(e, el)
33017 e.preventDefault();
33019 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33023 if(this.bgimage.length && this.html.length){
33024 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33028 leave: function(e, el)
33030 e.preventDefault();
33032 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33036 if(this.bgimage.length && this.html.length){
33037 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33041 onTouchStart: function(e, el)
33043 // e.preventDefault();
33045 this.touchmoved = false;
33047 if(!this.isFitContainer){
33051 if(!this.bgimage.length || !this.html.length){
33055 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33057 this.timer = new Date().getTime();
33061 onTouchMove: function(e, el)
33063 this.touchmoved = true;
33066 onContextMenu : function(e,el)
33068 e.preventDefault();
33069 e.stopPropagation();
33073 onTouchEnd: function(e, el)
33075 // e.preventDefault();
33077 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33084 if(!this.bgimage.length || !this.html.length){
33086 if(this.href.length){
33087 window.location.href = this.href;
33093 if(!this.isFitContainer){
33097 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33099 window.location.href = this.href;
33102 //selection on single brick only
33103 selectBrick : function() {
33105 if (!this.parentId) {
33109 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33110 var index = m.selectedBrick.indexOf(this.id);
33113 m.selectedBrick.splice(index,1);
33114 this.el.removeClass(this.activeClass);
33118 for(var i = 0; i < m.selectedBrick.length; i++) {
33119 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33120 b.el.removeClass(b.activeClass);
33123 m.selectedBrick = [];
33125 m.selectedBrick.push(this.id);
33126 this.el.addClass(this.activeClass);
33130 isSelected : function(){
33131 return this.el.hasClass(this.activeClass);
33136 Roo.apply(Roo.bootstrap.MasonryBrick, {
33139 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33141 * register a Masonry Brick
33142 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33145 register : function(brick)
33147 //this.groups[brick.id] = brick;
33148 this.groups.add(brick.id, brick);
33151 * fetch a masonry brick based on the masonry brick ID
33152 * @param {string} the masonry brick to add
33153 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33156 get: function(brick_id)
33158 // if (typeof(this.groups[brick_id]) == 'undefined') {
33161 // return this.groups[brick_id] ;
33163 if(this.groups.key(brick_id)) {
33164 return this.groups.key(brick_id);
33182 * @class Roo.bootstrap.Brick
33183 * @extends Roo.bootstrap.Component
33184 * Bootstrap Brick class
33187 * Create a new Brick
33188 * @param {Object} config The config object
33191 Roo.bootstrap.Brick = function(config){
33192 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33198 * When a Brick is click
33199 * @param {Roo.bootstrap.Brick} this
33200 * @param {Roo.EventObject} e
33206 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33209 * @cfg {String} title
33213 * @cfg {String} html
33217 * @cfg {String} bgimage
33221 * @cfg {String} cls
33225 * @cfg {String} href
33229 * @cfg {String} video
33233 * @cfg {Boolean} square
33237 getAutoCreate : function()
33239 var cls = 'roo-brick';
33241 if(this.href.length){
33242 cls += ' roo-brick-link';
33245 if(this.bgimage.length){
33246 cls += ' roo-brick-image';
33249 if(!this.html.length && !this.bgimage.length){
33250 cls += ' roo-brick-center-title';
33253 if(!this.html.length && this.bgimage.length){
33254 cls += ' roo-brick-bottom-title';
33258 cls += ' ' + this.cls;
33262 tag: (this.href.length) ? 'a' : 'div',
33267 cls: 'roo-brick-paragraph',
33273 if(this.href.length){
33274 cfg.href = this.href;
33277 var cn = cfg.cn[0].cn;
33279 if(this.title.length){
33282 cls: 'roo-brick-title',
33287 if(this.html.length){
33290 cls: 'roo-brick-text',
33297 if(this.bgimage.length){
33300 cls: 'roo-brick-image-view',
33308 initEvents: function()
33310 if(this.title.length || this.html.length){
33311 this.el.on('mouseenter' ,this.enter, this);
33312 this.el.on('mouseleave', this.leave, this);
33315 Roo.EventManager.onWindowResize(this.resize, this);
33317 if(this.bgimage.length){
33318 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33319 this.imageEl.on('load', this.onImageLoad, this);
33326 onImageLoad : function()
33331 resize : function()
33333 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33335 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33337 if(this.bgimage.length){
33338 var image = this.el.select('.roo-brick-image-view', true).first();
33340 image.setWidth(paragraph.getWidth());
33343 image.setHeight(paragraph.getWidth());
33346 this.el.setHeight(image.getHeight());
33347 paragraph.setHeight(image.getHeight());
33353 enter: function(e, el)
33355 e.preventDefault();
33357 if(this.bgimage.length){
33358 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33359 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33363 leave: function(e, el)
33365 e.preventDefault();
33367 if(this.bgimage.length){
33368 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33369 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33384 * @class Roo.bootstrap.NumberField
33385 * @extends Roo.bootstrap.Input
33386 * Bootstrap NumberField class
33392 * Create a new NumberField
33393 * @param {Object} config The config object
33396 Roo.bootstrap.NumberField = function(config){
33397 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33400 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33403 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33405 allowDecimals : true,
33407 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33409 decimalSeparator : ".",
33411 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33413 decimalPrecision : 2,
33415 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33417 allowNegative : true,
33420 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33424 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33426 minValue : Number.NEGATIVE_INFINITY,
33428 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33430 maxValue : Number.MAX_VALUE,
33432 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33434 minText : "The minimum value for this field is {0}",
33436 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33438 maxText : "The maximum value for this field is {0}",
33440 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33441 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33443 nanText : "{0} is not a valid number",
33445 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33447 thousandsDelimiter : false,
33449 * @cfg {String} valueAlign alignment of value
33451 valueAlign : "left",
33453 getAutoCreate : function()
33455 var hiddenInput = {
33459 cls: 'hidden-number-input'
33463 hiddenInput.name = this.name;
33468 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33470 this.name = hiddenInput.name;
33472 if(cfg.cn.length > 0) {
33473 cfg.cn.push(hiddenInput);
33480 initEvents : function()
33482 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33484 var allowed = "0123456789";
33486 if(this.allowDecimals){
33487 allowed += this.decimalSeparator;
33490 if(this.allowNegative){
33494 if(this.thousandsDelimiter) {
33498 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33500 var keyPress = function(e){
33502 var k = e.getKey();
33504 var c = e.getCharCode();
33507 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33508 allowed.indexOf(String.fromCharCode(c)) === -1
33514 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33518 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33523 this.el.on("keypress", keyPress, this);
33526 validateValue : function(value)
33529 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33533 var num = this.parseValue(value);
33536 this.markInvalid(String.format(this.nanText, value));
33540 if(num < this.minValue){
33541 this.markInvalid(String.format(this.minText, this.minValue));
33545 if(num > this.maxValue){
33546 this.markInvalid(String.format(this.maxText, this.maxValue));
33553 getValue : function()
33555 var v = this.hiddenEl().getValue();
33557 return this.fixPrecision(this.parseValue(v));
33560 parseValue : function(value)
33562 if(this.thousandsDelimiter) {
33564 r = new RegExp(",", "g");
33565 value = value.replace(r, "");
33568 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33569 return isNaN(value) ? '' : value;
33572 fixPrecision : function(value)
33574 if(this.thousandsDelimiter) {
33576 r = new RegExp(",", "g");
33577 value = value.replace(r, "");
33580 var nan = isNaN(value);
33582 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33583 return nan ? '' : value;
33585 return parseFloat(value).toFixed(this.decimalPrecision);
33588 setValue : function(v)
33590 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33596 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33598 this.inputEl().dom.value = (v == '') ? '' :
33599 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33601 if(!this.allowZero && v === '0') {
33602 this.hiddenEl().dom.value = '';
33603 this.inputEl().dom.value = '';
33610 decimalPrecisionFcn : function(v)
33612 return Math.floor(v);
33615 beforeBlur : function()
33617 var v = this.parseValue(this.getRawValue());
33619 if(v || v === 0 || v === ''){
33624 hiddenEl : function()
33626 return this.el.select('input.hidden-number-input',true).first();
33638 * @class Roo.bootstrap.DocumentSlider
33639 * @extends Roo.bootstrap.Component
33640 * Bootstrap DocumentSlider class
33643 * Create a new DocumentViewer
33644 * @param {Object} config The config object
33647 Roo.bootstrap.DocumentSlider = function(config){
33648 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33655 * Fire after initEvent
33656 * @param {Roo.bootstrap.DocumentSlider} this
33661 * Fire after update
33662 * @param {Roo.bootstrap.DocumentSlider} this
33668 * @param {Roo.bootstrap.DocumentSlider} this
33674 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33680 getAutoCreate : function()
33684 cls : 'roo-document-slider',
33688 cls : 'roo-document-slider-header',
33692 cls : 'roo-document-slider-header-title'
33698 cls : 'roo-document-slider-body',
33702 cls : 'roo-document-slider-prev',
33706 cls : 'fa fa-chevron-left'
33712 cls : 'roo-document-slider-thumb',
33716 cls : 'roo-document-slider-image'
33722 cls : 'roo-document-slider-next',
33726 cls : 'fa fa-chevron-right'
33738 initEvents : function()
33740 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33741 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33743 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33744 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33746 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33747 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33749 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33750 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33752 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33753 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33755 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33756 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33758 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33759 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33761 this.thumbEl.on('click', this.onClick, this);
33763 this.prevIndicator.on('click', this.prev, this);
33765 this.nextIndicator.on('click', this.next, this);
33769 initial : function()
33771 if(this.files.length){
33772 this.indicator = 1;
33776 this.fireEvent('initial', this);
33779 update : function()
33781 this.imageEl.attr('src', this.files[this.indicator - 1]);
33783 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33785 this.prevIndicator.show();
33787 if(this.indicator == 1){
33788 this.prevIndicator.hide();
33791 this.nextIndicator.show();
33793 if(this.indicator == this.files.length){
33794 this.nextIndicator.hide();
33797 this.thumbEl.scrollTo('top');
33799 this.fireEvent('update', this);
33802 onClick : function(e)
33804 e.preventDefault();
33806 this.fireEvent('click', this);
33811 e.preventDefault();
33813 this.indicator = Math.max(1, this.indicator - 1);
33820 e.preventDefault();
33822 this.indicator = Math.min(this.files.length, this.indicator + 1);
33836 * @class Roo.bootstrap.RadioSet
33837 * @extends Roo.bootstrap.Input
33838 * Bootstrap RadioSet class
33839 * @cfg {String} indicatorpos (left|right) default left
33840 * @cfg {Boolean} inline (true|false) inline the element (default true)
33841 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33843 * Create a new RadioSet
33844 * @param {Object} config The config object
33847 Roo.bootstrap.RadioSet = function(config){
33849 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33853 Roo.bootstrap.RadioSet.register(this);
33858 * Fires when the element is checked or unchecked.
33859 * @param {Roo.bootstrap.RadioSet} this This radio
33860 * @param {Roo.bootstrap.Radio} item The checked item
33865 * Fires when the element is click.
33866 * @param {Roo.bootstrap.RadioSet} this This radio set
33867 * @param {Roo.bootstrap.Radio} item The checked item
33868 * @param {Roo.EventObject} e The event object
33875 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33883 indicatorpos : 'left',
33885 getAutoCreate : function()
33889 cls : 'roo-radio-set-label',
33893 html : this.fieldLabel
33898 if(this.indicatorpos == 'left'){
33901 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33902 tooltip : 'This field is required'
33907 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33908 tooltip : 'This field is required'
33914 cls : 'roo-radio-set-items'
33917 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33919 if (align === 'left' && this.fieldLabel.length) {
33922 cls : "roo-radio-set-right",
33928 if(this.labelWidth > 12){
33929 label.style = "width: " + this.labelWidth + 'px';
33932 if(this.labelWidth < 13 && this.labelmd == 0){
33933 this.labelmd = this.labelWidth;
33936 if(this.labellg > 0){
33937 label.cls += ' col-lg-' + this.labellg;
33938 items.cls += ' col-lg-' + (12 - this.labellg);
33941 if(this.labelmd > 0){
33942 label.cls += ' col-md-' + this.labelmd;
33943 items.cls += ' col-md-' + (12 - this.labelmd);
33946 if(this.labelsm > 0){
33947 label.cls += ' col-sm-' + this.labelsm;
33948 items.cls += ' col-sm-' + (12 - this.labelsm);
33951 if(this.labelxs > 0){
33952 label.cls += ' col-xs-' + this.labelxs;
33953 items.cls += ' col-xs-' + (12 - this.labelxs);
33959 cls : 'roo-radio-set',
33963 cls : 'roo-radio-set-input',
33966 value : this.value ? this.value : ''
33973 if(this.weight.length){
33974 cfg.cls += ' roo-radio-' + this.weight;
33978 cfg.cls += ' roo-radio-set-inline';
33982 ['xs','sm','md','lg'].map(function(size){
33983 if (settings[size]) {
33984 cfg.cls += ' col-' + size + '-' + settings[size];
33992 initEvents : function()
33994 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33995 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33997 if(!this.fieldLabel.length){
33998 this.labelEl.hide();
34001 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34002 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34004 this.indicator = this.indicatorEl();
34006 if(this.indicator){
34007 this.indicator.addClass('invisible');
34010 this.originalValue = this.getValue();
34014 inputEl: function ()
34016 return this.el.select('.roo-radio-set-input', true).first();
34019 getChildContainer : function()
34021 return this.itemsEl;
34024 register : function(item)
34026 this.radioes.push(item);
34030 validate : function()
34032 if(this.getVisibilityEl().hasClass('hidden')){
34038 Roo.each(this.radioes, function(i){
34047 if(this.allowBlank) {
34051 if(this.disabled || valid){
34056 this.markInvalid();
34061 markValid : function()
34063 if(this.labelEl.isVisible(true)){
34064 this.indicatorEl().removeClass('visible');
34065 this.indicatorEl().addClass('invisible');
34068 this.el.removeClass([this.invalidClass, this.validClass]);
34069 this.el.addClass(this.validClass);
34071 this.fireEvent('valid', this);
34074 markInvalid : function(msg)
34076 if(this.allowBlank || this.disabled){
34080 if(this.labelEl.isVisible(true)){
34081 this.indicatorEl().removeClass('invisible');
34082 this.indicatorEl().addClass('visible');
34085 this.el.removeClass([this.invalidClass, this.validClass]);
34086 this.el.addClass(this.invalidClass);
34088 this.fireEvent('invalid', this, msg);
34092 setValue : function(v, suppressEvent)
34094 if(this.value === v){
34101 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34104 Roo.each(this.radioes, function(i){
34106 i.el.removeClass('checked');
34109 Roo.each(this.radioes, function(i){
34111 if(i.value === v || i.value.toString() === v.toString()){
34113 i.el.addClass('checked');
34115 if(suppressEvent !== true){
34116 this.fireEvent('check', this, i);
34127 clearInvalid : function(){
34129 if(!this.el || this.preventMark){
34133 this.el.removeClass([this.invalidClass]);
34135 this.fireEvent('valid', this);
34140 Roo.apply(Roo.bootstrap.RadioSet, {
34144 register : function(set)
34146 this.groups[set.name] = set;
34149 get: function(name)
34151 if (typeof(this.groups[name]) == 'undefined') {
34155 return this.groups[name] ;
34161 * Ext JS Library 1.1.1
34162 * Copyright(c) 2006-2007, Ext JS, LLC.
34164 * Originally Released Under LGPL - original licence link has changed is not relivant.
34167 * <script type="text/javascript">
34172 * @class Roo.bootstrap.SplitBar
34173 * @extends Roo.util.Observable
34174 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34178 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34179 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34180 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34181 split.minSize = 100;
34182 split.maxSize = 600;
34183 split.animate = true;
34184 split.on('moved', splitterMoved);
34187 * Create a new SplitBar
34188 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34189 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34190 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34191 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34192 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34193 position of the SplitBar).
34195 Roo.bootstrap.SplitBar = function(cfg){
34200 // dragElement : elm
34201 // resizingElement: el,
34203 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34204 // placement : Roo.bootstrap.SplitBar.LEFT ,
34205 // existingProxy ???
34208 this.el = Roo.get(cfg.dragElement, true);
34209 this.el.dom.unselectable = "on";
34211 this.resizingEl = Roo.get(cfg.resizingElement, true);
34215 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34216 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34219 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34222 * The minimum size of the resizing element. (Defaults to 0)
34228 * The maximum size of the resizing element. (Defaults to 2000)
34231 this.maxSize = 2000;
34234 * Whether to animate the transition to the new size
34237 this.animate = false;
34240 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34243 this.useShim = false;
34248 if(!cfg.existingProxy){
34250 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34252 this.proxy = Roo.get(cfg.existingProxy).dom;
34255 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34258 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34261 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34264 this.dragSpecs = {};
34267 * @private The adapter to use to positon and resize elements
34269 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34270 this.adapter.init(this);
34272 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34274 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34275 this.el.addClass("roo-splitbar-h");
34278 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34279 this.el.addClass("roo-splitbar-v");
34285 * Fires when the splitter is moved (alias for {@link #event-moved})
34286 * @param {Roo.bootstrap.SplitBar} this
34287 * @param {Number} newSize the new width or height
34292 * Fires when the splitter is moved
34293 * @param {Roo.bootstrap.SplitBar} this
34294 * @param {Number} newSize the new width or height
34298 * @event beforeresize
34299 * Fires before the splitter is dragged
34300 * @param {Roo.bootstrap.SplitBar} this
34302 "beforeresize" : true,
34304 "beforeapply" : true
34307 Roo.util.Observable.call(this);
34310 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34311 onStartProxyDrag : function(x, y){
34312 this.fireEvent("beforeresize", this);
34314 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34316 o.enableDisplayMode("block");
34317 // all splitbars share the same overlay
34318 Roo.bootstrap.SplitBar.prototype.overlay = o;
34320 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34321 this.overlay.show();
34322 Roo.get(this.proxy).setDisplayed("block");
34323 var size = this.adapter.getElementSize(this);
34324 this.activeMinSize = this.getMinimumSize();;
34325 this.activeMaxSize = this.getMaximumSize();;
34326 var c1 = size - this.activeMinSize;
34327 var c2 = Math.max(this.activeMaxSize - size, 0);
34328 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34329 this.dd.resetConstraints();
34330 this.dd.setXConstraint(
34331 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34332 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34334 this.dd.setYConstraint(0, 0);
34336 this.dd.resetConstraints();
34337 this.dd.setXConstraint(0, 0);
34338 this.dd.setYConstraint(
34339 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34340 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34343 this.dragSpecs.startSize = size;
34344 this.dragSpecs.startPoint = [x, y];
34345 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34349 * @private Called after the drag operation by the DDProxy
34351 onEndProxyDrag : function(e){
34352 Roo.get(this.proxy).setDisplayed(false);
34353 var endPoint = Roo.lib.Event.getXY(e);
34355 this.overlay.hide();
34358 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34359 newSize = this.dragSpecs.startSize +
34360 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34361 endPoint[0] - this.dragSpecs.startPoint[0] :
34362 this.dragSpecs.startPoint[0] - endPoint[0]
34365 newSize = this.dragSpecs.startSize +
34366 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34367 endPoint[1] - this.dragSpecs.startPoint[1] :
34368 this.dragSpecs.startPoint[1] - endPoint[1]
34371 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34372 if(newSize != this.dragSpecs.startSize){
34373 if(this.fireEvent('beforeapply', this, newSize) !== false){
34374 this.adapter.setElementSize(this, newSize);
34375 this.fireEvent("moved", this, newSize);
34376 this.fireEvent("resize", this, newSize);
34382 * Get the adapter this SplitBar uses
34383 * @return The adapter object
34385 getAdapter : function(){
34386 return this.adapter;
34390 * Set the adapter this SplitBar uses
34391 * @param {Object} adapter A SplitBar adapter object
34393 setAdapter : function(adapter){
34394 this.adapter = adapter;
34395 this.adapter.init(this);
34399 * Gets the minimum size for the resizing element
34400 * @return {Number} The minimum size
34402 getMinimumSize : function(){
34403 return this.minSize;
34407 * Sets the minimum size for the resizing element
34408 * @param {Number} minSize The minimum size
34410 setMinimumSize : function(minSize){
34411 this.minSize = minSize;
34415 * Gets the maximum size for the resizing element
34416 * @return {Number} The maximum size
34418 getMaximumSize : function(){
34419 return this.maxSize;
34423 * Sets the maximum size for the resizing element
34424 * @param {Number} maxSize The maximum size
34426 setMaximumSize : function(maxSize){
34427 this.maxSize = maxSize;
34431 * Sets the initialize size for the resizing element
34432 * @param {Number} size The initial size
34434 setCurrentSize : function(size){
34435 var oldAnimate = this.animate;
34436 this.animate = false;
34437 this.adapter.setElementSize(this, size);
34438 this.animate = oldAnimate;
34442 * Destroy this splitbar.
34443 * @param {Boolean} removeEl True to remove the element
34445 destroy : function(removeEl){
34447 this.shim.remove();
34450 this.proxy.parentNode.removeChild(this.proxy);
34458 * @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.
34460 Roo.bootstrap.SplitBar.createProxy = function(dir){
34461 var proxy = new Roo.Element(document.createElement("div"));
34462 proxy.unselectable();
34463 var cls = 'roo-splitbar-proxy';
34464 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34465 document.body.appendChild(proxy.dom);
34470 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34471 * Default Adapter. It assumes the splitter and resizing element are not positioned
34472 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34474 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34477 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34478 // do nothing for now
34479 init : function(s){
34483 * Called before drag operations to get the current size of the resizing element.
34484 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34486 getElementSize : function(s){
34487 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34488 return s.resizingEl.getWidth();
34490 return s.resizingEl.getHeight();
34495 * Called after drag operations to set the size of the resizing element.
34496 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34497 * @param {Number} newSize The new size to set
34498 * @param {Function} onComplete A function to be invoked when resizing is complete
34500 setElementSize : function(s, newSize, onComplete){
34501 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34503 s.resizingEl.setWidth(newSize);
34505 onComplete(s, newSize);
34508 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34513 s.resizingEl.setHeight(newSize);
34515 onComplete(s, newSize);
34518 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34525 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34526 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34527 * Adapter that moves the splitter element to align with the resized sizing element.
34528 * Used with an absolute positioned SplitBar.
34529 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34530 * document.body, make sure you assign an id to the body element.
34532 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34533 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34534 this.container = Roo.get(container);
34537 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34538 init : function(s){
34539 this.basic.init(s);
34542 getElementSize : function(s){
34543 return this.basic.getElementSize(s);
34546 setElementSize : function(s, newSize, onComplete){
34547 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34550 moveSplitter : function(s){
34551 var yes = Roo.bootstrap.SplitBar;
34552 switch(s.placement){
34554 s.el.setX(s.resizingEl.getRight());
34557 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34560 s.el.setY(s.resizingEl.getBottom());
34563 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34570 * Orientation constant - Create a vertical SplitBar
34574 Roo.bootstrap.SplitBar.VERTICAL = 1;
34577 * Orientation constant - Create a horizontal SplitBar
34581 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34584 * Placement constant - The resizing element is to the left of the splitter element
34588 Roo.bootstrap.SplitBar.LEFT = 1;
34591 * Placement constant - The resizing element is to the right of the splitter element
34595 Roo.bootstrap.SplitBar.RIGHT = 2;
34598 * Placement constant - The resizing element is positioned above the splitter element
34602 Roo.bootstrap.SplitBar.TOP = 3;
34605 * Placement constant - The resizing element is positioned under splitter element
34609 Roo.bootstrap.SplitBar.BOTTOM = 4;
34610 Roo.namespace("Roo.bootstrap.layout");/*
34612 * Ext JS Library 1.1.1
34613 * Copyright(c) 2006-2007, Ext JS, LLC.
34615 * Originally Released Under LGPL - original licence link has changed is not relivant.
34618 * <script type="text/javascript">
34622 * @class Roo.bootstrap.layout.Manager
34623 * @extends Roo.bootstrap.Component
34624 * Base class for layout managers.
34626 Roo.bootstrap.layout.Manager = function(config)
34628 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34634 /** false to disable window resize monitoring @type Boolean */
34635 this.monitorWindowResize = true;
34640 * Fires when a layout is performed.
34641 * @param {Roo.LayoutManager} this
34645 * @event regionresized
34646 * Fires when the user resizes a region.
34647 * @param {Roo.LayoutRegion} region The resized region
34648 * @param {Number} newSize The new size (width for east/west, height for north/south)
34650 "regionresized" : true,
34652 * @event regioncollapsed
34653 * Fires when a region is collapsed.
34654 * @param {Roo.LayoutRegion} region The collapsed region
34656 "regioncollapsed" : true,
34658 * @event regionexpanded
34659 * Fires when a region is expanded.
34660 * @param {Roo.LayoutRegion} region The expanded region
34662 "regionexpanded" : true
34664 this.updating = false;
34667 this.el = Roo.get(config.el);
34673 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34678 monitorWindowResize : true,
34684 onRender : function(ct, position)
34687 this.el = Roo.get(ct);
34690 //this.fireEvent('render',this);
34694 initEvents: function()
34698 // ie scrollbar fix
34699 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34700 document.body.scroll = "no";
34701 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34702 this.el.position('relative');
34704 this.id = this.el.id;
34705 this.el.addClass("roo-layout-container");
34706 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34707 if(this.el.dom != document.body ) {
34708 this.el.on('resize', this.layout,this);
34709 this.el.on('show', this.layout,this);
34715 * Returns true if this layout is currently being updated
34716 * @return {Boolean}
34718 isUpdating : function(){
34719 return this.updating;
34723 * Suspend the LayoutManager from doing auto-layouts while
34724 * making multiple add or remove calls
34726 beginUpdate : function(){
34727 this.updating = true;
34731 * Restore auto-layouts and optionally disable the manager from performing a layout
34732 * @param {Boolean} noLayout true to disable a layout update
34734 endUpdate : function(noLayout){
34735 this.updating = false;
34741 layout: function(){
34745 onRegionResized : function(region, newSize){
34746 this.fireEvent("regionresized", region, newSize);
34750 onRegionCollapsed : function(region){
34751 this.fireEvent("regioncollapsed", region);
34754 onRegionExpanded : function(region){
34755 this.fireEvent("regionexpanded", region);
34759 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34760 * performs box-model adjustments.
34761 * @return {Object} The size as an object {width: (the width), height: (the height)}
34763 getViewSize : function()
34766 if(this.el.dom != document.body){
34767 size = this.el.getSize();
34769 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34771 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34772 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34777 * Returns the Element this layout is bound to.
34778 * @return {Roo.Element}
34780 getEl : function(){
34785 * Returns the specified region.
34786 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34787 * @return {Roo.LayoutRegion}
34789 getRegion : function(target){
34790 return this.regions[target.toLowerCase()];
34793 onWindowResize : function(){
34794 if(this.monitorWindowResize){
34801 * Ext JS Library 1.1.1
34802 * Copyright(c) 2006-2007, Ext JS, LLC.
34804 * Originally Released Under LGPL - original licence link has changed is not relivant.
34807 * <script type="text/javascript">
34810 * @class Roo.bootstrap.layout.Border
34811 * @extends Roo.bootstrap.layout.Manager
34812 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34813 * please see: examples/bootstrap/nested.html<br><br>
34815 <b>The container the layout is rendered into can be either the body element or any other element.
34816 If it is not the body element, the container needs to either be an absolute positioned element,
34817 or you will need to add "position:relative" to the css of the container. You will also need to specify
34818 the container size if it is not the body element.</b>
34821 * Create a new Border
34822 * @param {Object} config Configuration options
34824 Roo.bootstrap.layout.Border = function(config){
34825 config = config || {};
34826 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34830 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34831 if(config[region]){
34832 config[region].region = region;
34833 this.addRegion(config[region]);
34839 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34841 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34843 * Creates and adds a new region if it doesn't already exist.
34844 * @param {String} target The target region key (north, south, east, west or center).
34845 * @param {Object} config The regions config object
34846 * @return {BorderLayoutRegion} The new region
34848 addRegion : function(config)
34850 if(!this.regions[config.region]){
34851 var r = this.factory(config);
34852 this.bindRegion(r);
34854 return this.regions[config.region];
34858 bindRegion : function(r){
34859 this.regions[r.config.region] = r;
34861 r.on("visibilitychange", this.layout, this);
34862 r.on("paneladded", this.layout, this);
34863 r.on("panelremoved", this.layout, this);
34864 r.on("invalidated", this.layout, this);
34865 r.on("resized", this.onRegionResized, this);
34866 r.on("collapsed", this.onRegionCollapsed, this);
34867 r.on("expanded", this.onRegionExpanded, this);
34871 * Performs a layout update.
34873 layout : function()
34875 if(this.updating) {
34879 // render all the rebions if they have not been done alreayd?
34880 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34881 if(this.regions[region] && !this.regions[region].bodyEl){
34882 this.regions[region].onRender(this.el)
34886 var size = this.getViewSize();
34887 var w = size.width;
34888 var h = size.height;
34893 //var x = 0, y = 0;
34895 var rs = this.regions;
34896 var north = rs["north"];
34897 var south = rs["south"];
34898 var west = rs["west"];
34899 var east = rs["east"];
34900 var center = rs["center"];
34901 //if(this.hideOnLayout){ // not supported anymore
34902 //c.el.setStyle("display", "none");
34904 if(north && north.isVisible()){
34905 var b = north.getBox();
34906 var m = north.getMargins();
34907 b.width = w - (m.left+m.right);
34910 centerY = b.height + b.y + m.bottom;
34911 centerH -= centerY;
34912 north.updateBox(this.safeBox(b));
34914 if(south && south.isVisible()){
34915 var b = south.getBox();
34916 var m = south.getMargins();
34917 b.width = w - (m.left+m.right);
34919 var totalHeight = (b.height + m.top + m.bottom);
34920 b.y = h - totalHeight + m.top;
34921 centerH -= totalHeight;
34922 south.updateBox(this.safeBox(b));
34924 if(west && west.isVisible()){
34925 var b = west.getBox();
34926 var m = west.getMargins();
34927 b.height = centerH - (m.top+m.bottom);
34929 b.y = centerY + m.top;
34930 var totalWidth = (b.width + m.left + m.right);
34931 centerX += totalWidth;
34932 centerW -= totalWidth;
34933 west.updateBox(this.safeBox(b));
34935 if(east && east.isVisible()){
34936 var b = east.getBox();
34937 var m = east.getMargins();
34938 b.height = centerH - (m.top+m.bottom);
34939 var totalWidth = (b.width + m.left + m.right);
34940 b.x = w - totalWidth + m.left;
34941 b.y = centerY + m.top;
34942 centerW -= totalWidth;
34943 east.updateBox(this.safeBox(b));
34946 var m = center.getMargins();
34948 x: centerX + m.left,
34949 y: centerY + m.top,
34950 width: centerW - (m.left+m.right),
34951 height: centerH - (m.top+m.bottom)
34953 //if(this.hideOnLayout){
34954 //center.el.setStyle("display", "block");
34956 center.updateBox(this.safeBox(centerBox));
34959 this.fireEvent("layout", this);
34963 safeBox : function(box){
34964 box.width = Math.max(0, box.width);
34965 box.height = Math.max(0, box.height);
34970 * Adds a ContentPanel (or subclass) to this layout.
34971 * @param {String} target The target region key (north, south, east, west or center).
34972 * @param {Roo.ContentPanel} panel The panel to add
34973 * @return {Roo.ContentPanel} The added panel
34975 add : function(target, panel){
34977 target = target.toLowerCase();
34978 return this.regions[target].add(panel);
34982 * Remove a ContentPanel (or subclass) to this layout.
34983 * @param {String} target The target region key (north, south, east, west or center).
34984 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34985 * @return {Roo.ContentPanel} The removed panel
34987 remove : function(target, panel){
34988 target = target.toLowerCase();
34989 return this.regions[target].remove(panel);
34993 * Searches all regions for a panel with the specified id
34994 * @param {String} panelId
34995 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34997 findPanel : function(panelId){
34998 var rs = this.regions;
34999 for(var target in rs){
35000 if(typeof rs[target] != "function"){
35001 var p = rs[target].getPanel(panelId);
35011 * Searches all regions for a panel with the specified id and activates (shows) it.
35012 * @param {String/ContentPanel} panelId The panels id or the panel itself
35013 * @return {Roo.ContentPanel} The shown panel or null
35015 showPanel : function(panelId) {
35016 var rs = this.regions;
35017 for(var target in rs){
35018 var r = rs[target];
35019 if(typeof r != "function"){
35020 if(r.hasPanel(panelId)){
35021 return r.showPanel(panelId);
35029 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35030 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35033 restoreState : function(provider){
35035 provider = Roo.state.Manager;
35037 var sm = new Roo.LayoutStateManager();
35038 sm.init(this, provider);
35044 * Adds a xtype elements to the layout.
35048 xtype : 'ContentPanel',
35055 xtype : 'NestedLayoutPanel',
35061 items : [ ... list of content panels or nested layout panels.. ]
35065 * @param {Object} cfg Xtype definition of item to add.
35067 addxtype : function(cfg)
35069 // basically accepts a pannel...
35070 // can accept a layout region..!?!?
35071 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35074 // theory? children can only be panels??
35076 //if (!cfg.xtype.match(/Panel$/)) {
35081 if (typeof(cfg.region) == 'undefined') {
35082 Roo.log("Failed to add Panel, region was not set");
35086 var region = cfg.region;
35092 xitems = cfg.items;
35099 case 'Content': // ContentPanel (el, cfg)
35100 case 'Scroll': // ContentPanel (el, cfg)
35102 cfg.autoCreate = true;
35103 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35105 // var el = this.el.createChild();
35106 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35109 this.add(region, ret);
35113 case 'TreePanel': // our new panel!
35114 cfg.el = this.el.createChild();
35115 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35116 this.add(region, ret);
35121 // create a new Layout (which is a Border Layout...
35123 var clayout = cfg.layout;
35124 clayout.el = this.el.createChild();
35125 clayout.items = clayout.items || [];
35129 // replace this exitems with the clayout ones..
35130 xitems = clayout.items;
35132 // force background off if it's in center...
35133 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35134 cfg.background = false;
35136 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35139 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35140 //console.log('adding nested layout panel ' + cfg.toSource());
35141 this.add(region, ret);
35142 nb = {}; /// find first...
35147 // needs grid and region
35149 //var el = this.getRegion(region).el.createChild();
35151 *var el = this.el.createChild();
35152 // create the grid first...
35153 cfg.grid.container = el;
35154 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35157 if (region == 'center' && this.active ) {
35158 cfg.background = false;
35161 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35163 this.add(region, ret);
35165 if (cfg.background) {
35166 // render grid on panel activation (if panel background)
35167 ret.on('activate', function(gp) {
35168 if (!gp.grid.rendered) {
35169 // gp.grid.render(el);
35173 // cfg.grid.render(el);
35179 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35180 // it was the old xcomponent building that caused this before.
35181 // espeically if border is the top element in the tree.
35191 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35193 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35194 this.add(region, ret);
35198 throw "Can not add '" + cfg.xtype + "' to Border";
35204 this.beginUpdate();
35208 Roo.each(xitems, function(i) {
35209 region = nb && i.region ? i.region : false;
35211 var add = ret.addxtype(i);
35214 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35215 if (!i.background) {
35216 abn[region] = nb[region] ;
35223 // make the last non-background panel active..
35224 //if (nb) { Roo.log(abn); }
35227 for(var r in abn) {
35228 region = this.getRegion(r);
35230 // tried using nb[r], but it does not work..
35232 region.showPanel(abn[r]);
35243 factory : function(cfg)
35246 var validRegions = Roo.bootstrap.layout.Border.regions;
35248 var target = cfg.region;
35251 var r = Roo.bootstrap.layout;
35255 return new r.North(cfg);
35257 return new r.South(cfg);
35259 return new r.East(cfg);
35261 return new r.West(cfg);
35263 return new r.Center(cfg);
35265 throw 'Layout region "'+target+'" not supported.';
35272 * Ext JS Library 1.1.1
35273 * Copyright(c) 2006-2007, Ext JS, LLC.
35275 * Originally Released Under LGPL - original licence link has changed is not relivant.
35278 * <script type="text/javascript">
35282 * @class Roo.bootstrap.layout.Basic
35283 * @extends Roo.util.Observable
35284 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35285 * and does not have a titlebar, tabs or any other features. All it does is size and position
35286 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35287 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35288 * @cfg {string} region the region that it inhabits..
35289 * @cfg {bool} skipConfig skip config?
35293 Roo.bootstrap.layout.Basic = function(config){
35295 this.mgr = config.mgr;
35297 this.position = config.region;
35299 var skipConfig = config.skipConfig;
35303 * @scope Roo.BasicLayoutRegion
35307 * @event beforeremove
35308 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35309 * @param {Roo.LayoutRegion} this
35310 * @param {Roo.ContentPanel} panel The panel
35311 * @param {Object} e The cancel event object
35313 "beforeremove" : true,
35315 * @event invalidated
35316 * Fires when the layout for this region is changed.
35317 * @param {Roo.LayoutRegion} this
35319 "invalidated" : true,
35321 * @event visibilitychange
35322 * Fires when this region is shown or hidden
35323 * @param {Roo.LayoutRegion} this
35324 * @param {Boolean} visibility true or false
35326 "visibilitychange" : true,
35328 * @event paneladded
35329 * Fires when a panel is added.
35330 * @param {Roo.LayoutRegion} this
35331 * @param {Roo.ContentPanel} panel The panel
35333 "paneladded" : true,
35335 * @event panelremoved
35336 * Fires when a panel is removed.
35337 * @param {Roo.LayoutRegion} this
35338 * @param {Roo.ContentPanel} panel The panel
35340 "panelremoved" : true,
35342 * @event beforecollapse
35343 * Fires when this region before collapse.
35344 * @param {Roo.LayoutRegion} this
35346 "beforecollapse" : true,
35349 * Fires when this region is collapsed.
35350 * @param {Roo.LayoutRegion} this
35352 "collapsed" : true,
35355 * Fires when this region is expanded.
35356 * @param {Roo.LayoutRegion} this
35361 * Fires when this region is slid into view.
35362 * @param {Roo.LayoutRegion} this
35364 "slideshow" : true,
35367 * Fires when this region slides out of view.
35368 * @param {Roo.LayoutRegion} this
35370 "slidehide" : true,
35372 * @event panelactivated
35373 * Fires when a panel is activated.
35374 * @param {Roo.LayoutRegion} this
35375 * @param {Roo.ContentPanel} panel The activated panel
35377 "panelactivated" : true,
35380 * Fires when the user resizes this region.
35381 * @param {Roo.LayoutRegion} this
35382 * @param {Number} newSize The new size (width for east/west, height for north/south)
35386 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35387 this.panels = new Roo.util.MixedCollection();
35388 this.panels.getKey = this.getPanelId.createDelegate(this);
35390 this.activePanel = null;
35391 // ensure listeners are added...
35393 if (config.listeners || config.events) {
35394 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35395 listeners : config.listeners || {},
35396 events : config.events || {}
35400 if(skipConfig !== true){
35401 this.applyConfig(config);
35405 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35407 getPanelId : function(p){
35411 applyConfig : function(config){
35412 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35413 this.config = config;
35418 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35419 * the width, for horizontal (north, south) the height.
35420 * @param {Number} newSize The new width or height
35422 resizeTo : function(newSize){
35423 var el = this.el ? this.el :
35424 (this.activePanel ? this.activePanel.getEl() : null);
35426 switch(this.position){
35429 el.setWidth(newSize);
35430 this.fireEvent("resized", this, newSize);
35434 el.setHeight(newSize);
35435 this.fireEvent("resized", this, newSize);
35441 getBox : function(){
35442 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35445 getMargins : function(){
35446 return this.margins;
35449 updateBox : function(box){
35451 var el = this.activePanel.getEl();
35452 el.dom.style.left = box.x + "px";
35453 el.dom.style.top = box.y + "px";
35454 this.activePanel.setSize(box.width, box.height);
35458 * Returns the container element for this region.
35459 * @return {Roo.Element}
35461 getEl : function(){
35462 return this.activePanel;
35466 * Returns true if this region is currently visible.
35467 * @return {Boolean}
35469 isVisible : function(){
35470 return this.activePanel ? true : false;
35473 setActivePanel : function(panel){
35474 panel = this.getPanel(panel);
35475 if(this.activePanel && this.activePanel != panel){
35476 this.activePanel.setActiveState(false);
35477 this.activePanel.getEl().setLeftTop(-10000,-10000);
35479 this.activePanel = panel;
35480 panel.setActiveState(true);
35482 panel.setSize(this.box.width, this.box.height);
35484 this.fireEvent("panelactivated", this, panel);
35485 this.fireEvent("invalidated");
35489 * Show the specified panel.
35490 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35491 * @return {Roo.ContentPanel} The shown panel or null
35493 showPanel : function(panel){
35494 panel = this.getPanel(panel);
35496 this.setActivePanel(panel);
35502 * Get the active panel for this region.
35503 * @return {Roo.ContentPanel} The active panel or null
35505 getActivePanel : function(){
35506 return this.activePanel;
35510 * Add the passed ContentPanel(s)
35511 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35512 * @return {Roo.ContentPanel} The panel added (if only one was added)
35514 add : function(panel){
35515 if(arguments.length > 1){
35516 for(var i = 0, len = arguments.length; i < len; i++) {
35517 this.add(arguments[i]);
35521 if(this.hasPanel(panel)){
35522 this.showPanel(panel);
35525 var el = panel.getEl();
35526 if(el.dom.parentNode != this.mgr.el.dom){
35527 this.mgr.el.dom.appendChild(el.dom);
35529 if(panel.setRegion){
35530 panel.setRegion(this);
35532 this.panels.add(panel);
35533 el.setStyle("position", "absolute");
35534 if(!panel.background){
35535 this.setActivePanel(panel);
35536 if(this.config.initialSize && this.panels.getCount()==1){
35537 this.resizeTo(this.config.initialSize);
35540 this.fireEvent("paneladded", this, panel);
35545 * Returns true if the panel is in this region.
35546 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35547 * @return {Boolean}
35549 hasPanel : function(panel){
35550 if(typeof panel == "object"){ // must be panel obj
35551 panel = panel.getId();
35553 return this.getPanel(panel) ? true : false;
35557 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35558 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35559 * @param {Boolean} preservePanel Overrides the config preservePanel option
35560 * @return {Roo.ContentPanel} The panel that was removed
35562 remove : function(panel, preservePanel){
35563 panel = this.getPanel(panel);
35568 this.fireEvent("beforeremove", this, panel, e);
35569 if(e.cancel === true){
35572 var panelId = panel.getId();
35573 this.panels.removeKey(panelId);
35578 * Returns the panel specified or null if it's not in this region.
35579 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35580 * @return {Roo.ContentPanel}
35582 getPanel : function(id){
35583 if(typeof id == "object"){ // must be panel obj
35586 return this.panels.get(id);
35590 * Returns this regions position (north/south/east/west/center).
35593 getPosition: function(){
35594 return this.position;
35598 * Ext JS Library 1.1.1
35599 * Copyright(c) 2006-2007, Ext JS, LLC.
35601 * Originally Released Under LGPL - original licence link has changed is not relivant.
35604 * <script type="text/javascript">
35608 * @class Roo.bootstrap.layout.Region
35609 * @extends Roo.bootstrap.layout.Basic
35610 * This class represents a region in a layout manager.
35612 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35613 * @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})
35614 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35615 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35616 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35617 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35618 * @cfg {String} title The title for the region (overrides panel titles)
35619 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35620 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35621 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35622 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35623 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35624 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35625 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35626 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35627 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35628 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35630 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35631 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35632 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35633 * @cfg {Number} width For East/West panels
35634 * @cfg {Number} height For North/South panels
35635 * @cfg {Boolean} split To show the splitter
35636 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35638 * @cfg {string} cls Extra CSS classes to add to region
35640 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35641 * @cfg {string} region the region that it inhabits..
35644 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35645 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35647 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35648 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35649 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35651 Roo.bootstrap.layout.Region = function(config)
35653 this.applyConfig(config);
35655 var mgr = config.mgr;
35656 var pos = config.region;
35657 config.skipConfig = true;
35658 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35661 this.onRender(mgr.el);
35664 this.visible = true;
35665 this.collapsed = false;
35666 this.unrendered_panels = [];
35669 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35671 position: '', // set by wrapper (eg. north/south etc..)
35672 unrendered_panels : null, // unrendered panels.
35673 createBody : function(){
35674 /** This region's body element
35675 * @type Roo.Element */
35676 this.bodyEl = this.el.createChild({
35678 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35682 onRender: function(ctr, pos)
35684 var dh = Roo.DomHelper;
35685 /** This region's container element
35686 * @type Roo.Element */
35687 this.el = dh.append(ctr.dom, {
35689 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35691 /** This region's title element
35692 * @type Roo.Element */
35694 this.titleEl = dh.append(this.el.dom,
35697 unselectable: "on",
35698 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35700 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35701 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35704 this.titleEl.enableDisplayMode();
35705 /** This region's title text element
35706 * @type HTMLElement */
35707 this.titleTextEl = this.titleEl.dom.firstChild;
35708 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35710 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35711 this.closeBtn.enableDisplayMode();
35712 this.closeBtn.on("click", this.closeClicked, this);
35713 this.closeBtn.hide();
35715 this.createBody(this.config);
35716 if(this.config.hideWhenEmpty){
35718 this.on("paneladded", this.validateVisibility, this);
35719 this.on("panelremoved", this.validateVisibility, this);
35721 if(this.autoScroll){
35722 this.bodyEl.setStyle("overflow", "auto");
35724 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35726 //if(c.titlebar !== false){
35727 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35728 this.titleEl.hide();
35730 this.titleEl.show();
35731 if(this.config.title){
35732 this.titleTextEl.innerHTML = this.config.title;
35736 if(this.config.collapsed){
35737 this.collapse(true);
35739 if(this.config.hidden){
35743 if (this.unrendered_panels && this.unrendered_panels.length) {
35744 for (var i =0;i< this.unrendered_panels.length; i++) {
35745 this.add(this.unrendered_panels[i]);
35747 this.unrendered_panels = null;
35753 applyConfig : function(c)
35756 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35757 var dh = Roo.DomHelper;
35758 if(c.titlebar !== false){
35759 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35760 this.collapseBtn.on("click", this.collapse, this);
35761 this.collapseBtn.enableDisplayMode();
35763 if(c.showPin === true || this.showPin){
35764 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35765 this.stickBtn.enableDisplayMode();
35766 this.stickBtn.on("click", this.expand, this);
35767 this.stickBtn.hide();
35772 /** This region's collapsed element
35773 * @type Roo.Element */
35776 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35777 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35780 if(c.floatable !== false){
35781 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35782 this.collapsedEl.on("click", this.collapseClick, this);
35785 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35786 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35787 id: "message", unselectable: "on", style:{"float":"left"}});
35788 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35790 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35791 this.expandBtn.on("click", this.expand, this);
35795 if(this.collapseBtn){
35796 this.collapseBtn.setVisible(c.collapsible == true);
35799 this.cmargins = c.cmargins || this.cmargins ||
35800 (this.position == "west" || this.position == "east" ?
35801 {top: 0, left: 2, right:2, bottom: 0} :
35802 {top: 2, left: 0, right:0, bottom: 2});
35804 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35807 this.bottomTabs = c.tabPosition != "top";
35809 this.autoScroll = c.autoScroll || false;
35814 this.duration = c.duration || .30;
35815 this.slideDuration = c.slideDuration || .45;
35820 * Returns true if this region is currently visible.
35821 * @return {Boolean}
35823 isVisible : function(){
35824 return this.visible;
35828 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35829 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35831 //setCollapsedTitle : function(title){
35832 // title = title || " ";
35833 // if(this.collapsedTitleTextEl){
35834 // this.collapsedTitleTextEl.innerHTML = title;
35838 getBox : function(){
35840 // if(!this.collapsed){
35841 b = this.el.getBox(false, true);
35843 // b = this.collapsedEl.getBox(false, true);
35848 getMargins : function(){
35849 return this.margins;
35850 //return this.collapsed ? this.cmargins : this.margins;
35853 highlight : function(){
35854 this.el.addClass("x-layout-panel-dragover");
35857 unhighlight : function(){
35858 this.el.removeClass("x-layout-panel-dragover");
35861 updateBox : function(box)
35863 if (!this.bodyEl) {
35864 return; // not rendered yet..
35868 if(!this.collapsed){
35869 this.el.dom.style.left = box.x + "px";
35870 this.el.dom.style.top = box.y + "px";
35871 this.updateBody(box.width, box.height);
35873 this.collapsedEl.dom.style.left = box.x + "px";
35874 this.collapsedEl.dom.style.top = box.y + "px";
35875 this.collapsedEl.setSize(box.width, box.height);
35878 this.tabs.autoSizeTabs();
35882 updateBody : function(w, h)
35885 this.el.setWidth(w);
35886 w -= this.el.getBorderWidth("rl");
35887 if(this.config.adjustments){
35888 w += this.config.adjustments[0];
35891 if(h !== null && h > 0){
35892 this.el.setHeight(h);
35893 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35894 h -= this.el.getBorderWidth("tb");
35895 if(this.config.adjustments){
35896 h += this.config.adjustments[1];
35898 this.bodyEl.setHeight(h);
35900 h = this.tabs.syncHeight(h);
35903 if(this.panelSize){
35904 w = w !== null ? w : this.panelSize.width;
35905 h = h !== null ? h : this.panelSize.height;
35907 if(this.activePanel){
35908 var el = this.activePanel.getEl();
35909 w = w !== null ? w : el.getWidth();
35910 h = h !== null ? h : el.getHeight();
35911 this.panelSize = {width: w, height: h};
35912 this.activePanel.setSize(w, h);
35914 if(Roo.isIE && this.tabs){
35915 this.tabs.el.repaint();
35920 * Returns the container element for this region.
35921 * @return {Roo.Element}
35923 getEl : function(){
35928 * Hides this region.
35931 //if(!this.collapsed){
35932 this.el.dom.style.left = "-2000px";
35935 // this.collapsedEl.dom.style.left = "-2000px";
35936 // this.collapsedEl.hide();
35938 this.visible = false;
35939 this.fireEvent("visibilitychange", this, false);
35943 * Shows this region if it was previously hidden.
35946 //if(!this.collapsed){
35949 // this.collapsedEl.show();
35951 this.visible = true;
35952 this.fireEvent("visibilitychange", this, true);
35955 closeClicked : function(){
35956 if(this.activePanel){
35957 this.remove(this.activePanel);
35961 collapseClick : function(e){
35963 e.stopPropagation();
35966 e.stopPropagation();
35972 * Collapses this region.
35973 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35976 collapse : function(skipAnim, skipCheck = false){
35977 if(this.collapsed) {
35981 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35983 this.collapsed = true;
35985 this.split.el.hide();
35987 if(this.config.animate && skipAnim !== true){
35988 this.fireEvent("invalidated", this);
35989 this.animateCollapse();
35991 this.el.setLocation(-20000,-20000);
35993 this.collapsedEl.show();
35994 this.fireEvent("collapsed", this);
35995 this.fireEvent("invalidated", this);
36001 animateCollapse : function(){
36006 * Expands this region if it was previously collapsed.
36007 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36008 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36011 expand : function(e, skipAnim){
36013 e.stopPropagation();
36015 if(!this.collapsed || this.el.hasActiveFx()) {
36019 this.afterSlideIn();
36022 this.collapsed = false;
36023 if(this.config.animate && skipAnim !== true){
36024 this.animateExpand();
36028 this.split.el.show();
36030 this.collapsedEl.setLocation(-2000,-2000);
36031 this.collapsedEl.hide();
36032 this.fireEvent("invalidated", this);
36033 this.fireEvent("expanded", this);
36037 animateExpand : function(){
36041 initTabs : function()
36043 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36045 var ts = new Roo.bootstrap.panel.Tabs({
36046 el: this.bodyEl.dom,
36047 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36048 disableTooltips: this.config.disableTabTips,
36049 toolbar : this.config.toolbar
36052 if(this.config.hideTabs){
36053 ts.stripWrap.setDisplayed(false);
36056 ts.resizeTabs = this.config.resizeTabs === true;
36057 ts.minTabWidth = this.config.minTabWidth || 40;
36058 ts.maxTabWidth = this.config.maxTabWidth || 250;
36059 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36060 ts.monitorResize = false;
36061 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36062 ts.bodyEl.addClass('roo-layout-tabs-body');
36063 this.panels.each(this.initPanelAsTab, this);
36066 initPanelAsTab : function(panel){
36067 var ti = this.tabs.addTab(
36071 this.config.closeOnTab && panel.isClosable(),
36074 if(panel.tabTip !== undefined){
36075 ti.setTooltip(panel.tabTip);
36077 ti.on("activate", function(){
36078 this.setActivePanel(panel);
36081 if(this.config.closeOnTab){
36082 ti.on("beforeclose", function(t, e){
36084 this.remove(panel);
36088 panel.tabItem = ti;
36093 updatePanelTitle : function(panel, title)
36095 if(this.activePanel == panel){
36096 this.updateTitle(title);
36099 var ti = this.tabs.getTab(panel.getEl().id);
36101 if(panel.tabTip !== undefined){
36102 ti.setTooltip(panel.tabTip);
36107 updateTitle : function(title){
36108 if(this.titleTextEl && !this.config.title){
36109 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36113 setActivePanel : function(panel)
36115 panel = this.getPanel(panel);
36116 if(this.activePanel && this.activePanel != panel){
36117 if(this.activePanel.setActiveState(false) === false){
36121 this.activePanel = panel;
36122 panel.setActiveState(true);
36123 if(this.panelSize){
36124 panel.setSize(this.panelSize.width, this.panelSize.height);
36127 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36129 this.updateTitle(panel.getTitle());
36131 this.fireEvent("invalidated", this);
36133 this.fireEvent("panelactivated", this, panel);
36137 * Shows the specified panel.
36138 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36139 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36141 showPanel : function(panel)
36143 panel = this.getPanel(panel);
36146 var tab = this.tabs.getTab(panel.getEl().id);
36147 if(tab.isHidden()){
36148 this.tabs.unhideTab(tab.id);
36152 this.setActivePanel(panel);
36159 * Get the active panel for this region.
36160 * @return {Roo.ContentPanel} The active panel or null
36162 getActivePanel : function(){
36163 return this.activePanel;
36166 validateVisibility : function(){
36167 if(this.panels.getCount() < 1){
36168 this.updateTitle(" ");
36169 this.closeBtn.hide();
36172 if(!this.isVisible()){
36179 * Adds the passed ContentPanel(s) to this region.
36180 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36181 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36183 add : function(panel)
36185 if(arguments.length > 1){
36186 for(var i = 0, len = arguments.length; i < len; i++) {
36187 this.add(arguments[i]);
36192 // if we have not been rendered yet, then we can not really do much of this..
36193 if (!this.bodyEl) {
36194 this.unrendered_panels.push(panel);
36201 if(this.hasPanel(panel)){
36202 this.showPanel(panel);
36205 panel.setRegion(this);
36206 this.panels.add(panel);
36207 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36208 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36209 // and hide them... ???
36210 this.bodyEl.dom.appendChild(panel.getEl().dom);
36211 if(panel.background !== true){
36212 this.setActivePanel(panel);
36214 this.fireEvent("paneladded", this, panel);
36221 this.initPanelAsTab(panel);
36225 if(panel.background !== true){
36226 this.tabs.activate(panel.getEl().id);
36228 this.fireEvent("paneladded", this, panel);
36233 * Hides the tab for the specified panel.
36234 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36236 hidePanel : function(panel){
36237 if(this.tabs && (panel = this.getPanel(panel))){
36238 this.tabs.hideTab(panel.getEl().id);
36243 * Unhides the tab for a previously hidden panel.
36244 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36246 unhidePanel : function(panel){
36247 if(this.tabs && (panel = this.getPanel(panel))){
36248 this.tabs.unhideTab(panel.getEl().id);
36252 clearPanels : function(){
36253 while(this.panels.getCount() > 0){
36254 this.remove(this.panels.first());
36259 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36260 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36261 * @param {Boolean} preservePanel Overrides the config preservePanel option
36262 * @return {Roo.ContentPanel} The panel that was removed
36264 remove : function(panel, preservePanel)
36266 panel = this.getPanel(panel);
36271 this.fireEvent("beforeremove", this, panel, e);
36272 if(e.cancel === true){
36275 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36276 var panelId = panel.getId();
36277 this.panels.removeKey(panelId);
36279 document.body.appendChild(panel.getEl().dom);
36282 this.tabs.removeTab(panel.getEl().id);
36283 }else if (!preservePanel){
36284 this.bodyEl.dom.removeChild(panel.getEl().dom);
36286 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36287 var p = this.panels.first();
36288 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36289 tempEl.appendChild(p.getEl().dom);
36290 this.bodyEl.update("");
36291 this.bodyEl.dom.appendChild(p.getEl().dom);
36293 this.updateTitle(p.getTitle());
36295 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36296 this.setActivePanel(p);
36298 panel.setRegion(null);
36299 if(this.activePanel == panel){
36300 this.activePanel = null;
36302 if(this.config.autoDestroy !== false && preservePanel !== true){
36303 try{panel.destroy();}catch(e){}
36305 this.fireEvent("panelremoved", this, panel);
36310 * Returns the TabPanel component used by this region
36311 * @return {Roo.TabPanel}
36313 getTabs : function(){
36317 createTool : function(parentEl, className){
36318 var btn = Roo.DomHelper.append(parentEl, {
36320 cls: "x-layout-tools-button",
36323 cls: "roo-layout-tools-button-inner " + className,
36327 btn.addClassOnOver("roo-layout-tools-button-over");
36332 * Ext JS Library 1.1.1
36333 * Copyright(c) 2006-2007, Ext JS, LLC.
36335 * Originally Released Under LGPL - original licence link has changed is not relivant.
36338 * <script type="text/javascript">
36344 * @class Roo.SplitLayoutRegion
36345 * @extends Roo.LayoutRegion
36346 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36348 Roo.bootstrap.layout.Split = function(config){
36349 this.cursor = config.cursor;
36350 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36353 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36355 splitTip : "Drag to resize.",
36356 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36357 useSplitTips : false,
36359 applyConfig : function(config){
36360 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36363 onRender : function(ctr,pos) {
36365 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36366 if(!this.config.split){
36371 var splitEl = Roo.DomHelper.append(ctr.dom, {
36373 id: this.el.id + "-split",
36374 cls: "roo-layout-split roo-layout-split-"+this.position,
36377 /** The SplitBar for this region
36378 * @type Roo.SplitBar */
36379 // does not exist yet...
36380 Roo.log([this.position, this.orientation]);
36382 this.split = new Roo.bootstrap.SplitBar({
36383 dragElement : splitEl,
36384 resizingElement: this.el,
36385 orientation : this.orientation
36388 this.split.on("moved", this.onSplitMove, this);
36389 this.split.useShim = this.config.useShim === true;
36390 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36391 if(this.useSplitTips){
36392 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36394 //if(config.collapsible){
36395 // this.split.el.on("dblclick", this.collapse, this);
36398 if(typeof this.config.minSize != "undefined"){
36399 this.split.minSize = this.config.minSize;
36401 if(typeof this.config.maxSize != "undefined"){
36402 this.split.maxSize = this.config.maxSize;
36404 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36405 this.hideSplitter();
36410 getHMaxSize : function(){
36411 var cmax = this.config.maxSize || 10000;
36412 var center = this.mgr.getRegion("center");
36413 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36416 getVMaxSize : function(){
36417 var cmax = this.config.maxSize || 10000;
36418 var center = this.mgr.getRegion("center");
36419 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36422 onSplitMove : function(split, newSize){
36423 this.fireEvent("resized", this, newSize);
36427 * Returns the {@link Roo.SplitBar} for this region.
36428 * @return {Roo.SplitBar}
36430 getSplitBar : function(){
36435 this.hideSplitter();
36436 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36439 hideSplitter : function(){
36441 this.split.el.setLocation(-2000,-2000);
36442 this.split.el.hide();
36448 this.split.el.show();
36450 Roo.bootstrap.layout.Split.superclass.show.call(this);
36453 beforeSlide: function(){
36454 if(Roo.isGecko){// firefox overflow auto bug workaround
36455 this.bodyEl.clip();
36457 this.tabs.bodyEl.clip();
36459 if(this.activePanel){
36460 this.activePanel.getEl().clip();
36462 if(this.activePanel.beforeSlide){
36463 this.activePanel.beforeSlide();
36469 afterSlide : function(){
36470 if(Roo.isGecko){// firefox overflow auto bug workaround
36471 this.bodyEl.unclip();
36473 this.tabs.bodyEl.unclip();
36475 if(this.activePanel){
36476 this.activePanel.getEl().unclip();
36477 if(this.activePanel.afterSlide){
36478 this.activePanel.afterSlide();
36484 initAutoHide : function(){
36485 if(this.autoHide !== false){
36486 if(!this.autoHideHd){
36487 var st = new Roo.util.DelayedTask(this.slideIn, this);
36488 this.autoHideHd = {
36489 "mouseout": function(e){
36490 if(!e.within(this.el, true)){
36494 "mouseover" : function(e){
36500 this.el.on(this.autoHideHd);
36504 clearAutoHide : function(){
36505 if(this.autoHide !== false){
36506 this.el.un("mouseout", this.autoHideHd.mouseout);
36507 this.el.un("mouseover", this.autoHideHd.mouseover);
36511 clearMonitor : function(){
36512 Roo.get(document).un("click", this.slideInIf, this);
36515 // these names are backwards but not changed for compat
36516 slideOut : function(){
36517 if(this.isSlid || this.el.hasActiveFx()){
36520 this.isSlid = true;
36521 if(this.collapseBtn){
36522 this.collapseBtn.hide();
36524 this.closeBtnState = this.closeBtn.getStyle('display');
36525 this.closeBtn.hide();
36527 this.stickBtn.show();
36530 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36531 this.beforeSlide();
36532 this.el.setStyle("z-index", 10001);
36533 this.el.slideIn(this.getSlideAnchor(), {
36534 callback: function(){
36536 this.initAutoHide();
36537 Roo.get(document).on("click", this.slideInIf, this);
36538 this.fireEvent("slideshow", this);
36545 afterSlideIn : function(){
36546 this.clearAutoHide();
36547 this.isSlid = false;
36548 this.clearMonitor();
36549 this.el.setStyle("z-index", "");
36550 if(this.collapseBtn){
36551 this.collapseBtn.show();
36553 this.closeBtn.setStyle('display', this.closeBtnState);
36555 this.stickBtn.hide();
36557 this.fireEvent("slidehide", this);
36560 slideIn : function(cb){
36561 if(!this.isSlid || this.el.hasActiveFx()){
36565 this.isSlid = false;
36566 this.beforeSlide();
36567 this.el.slideOut(this.getSlideAnchor(), {
36568 callback: function(){
36569 this.el.setLeftTop(-10000, -10000);
36571 this.afterSlideIn();
36579 slideInIf : function(e){
36580 if(!e.within(this.el)){
36585 animateCollapse : function(){
36586 this.beforeSlide();
36587 this.el.setStyle("z-index", 20000);
36588 var anchor = this.getSlideAnchor();
36589 this.el.slideOut(anchor, {
36590 callback : function(){
36591 this.el.setStyle("z-index", "");
36592 this.collapsedEl.slideIn(anchor, {duration:.3});
36594 this.el.setLocation(-10000,-10000);
36596 this.fireEvent("collapsed", this);
36603 animateExpand : function(){
36604 this.beforeSlide();
36605 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36606 this.el.setStyle("z-index", 20000);
36607 this.collapsedEl.hide({
36610 this.el.slideIn(this.getSlideAnchor(), {
36611 callback : function(){
36612 this.el.setStyle("z-index", "");
36615 this.split.el.show();
36617 this.fireEvent("invalidated", this);
36618 this.fireEvent("expanded", this);
36646 getAnchor : function(){
36647 return this.anchors[this.position];
36650 getCollapseAnchor : function(){
36651 return this.canchors[this.position];
36654 getSlideAnchor : function(){
36655 return this.sanchors[this.position];
36658 getAlignAdj : function(){
36659 var cm = this.cmargins;
36660 switch(this.position){
36676 getExpandAdj : function(){
36677 var c = this.collapsedEl, cm = this.cmargins;
36678 switch(this.position){
36680 return [-(cm.right+c.getWidth()+cm.left), 0];
36683 return [cm.right+c.getWidth()+cm.left, 0];
36686 return [0, -(cm.top+cm.bottom+c.getHeight())];
36689 return [0, cm.top+cm.bottom+c.getHeight()];
36695 * Ext JS Library 1.1.1
36696 * Copyright(c) 2006-2007, Ext JS, LLC.
36698 * Originally Released Under LGPL - original licence link has changed is not relivant.
36701 * <script type="text/javascript">
36704 * These classes are private internal classes
36706 Roo.bootstrap.layout.Center = function(config){
36707 config.region = "center";
36708 Roo.bootstrap.layout.Region.call(this, config);
36709 this.visible = true;
36710 this.minWidth = config.minWidth || 20;
36711 this.minHeight = config.minHeight || 20;
36714 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36716 // center panel can't be hidden
36720 // center panel can't be hidden
36723 getMinWidth: function(){
36724 return this.minWidth;
36727 getMinHeight: function(){
36728 return this.minHeight;
36741 Roo.bootstrap.layout.North = function(config)
36743 config.region = 'north';
36744 config.cursor = 'n-resize';
36746 Roo.bootstrap.layout.Split.call(this, config);
36750 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36751 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36752 this.split.el.addClass("roo-layout-split-v");
36754 var size = config.initialSize || config.height;
36755 if(typeof size != "undefined"){
36756 this.el.setHeight(size);
36759 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36761 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36765 getBox : function(){
36766 if(this.collapsed){
36767 return this.collapsedEl.getBox();
36769 var box = this.el.getBox();
36771 box.height += this.split.el.getHeight();
36776 updateBox : function(box){
36777 if(this.split && !this.collapsed){
36778 box.height -= this.split.el.getHeight();
36779 this.split.el.setLeft(box.x);
36780 this.split.el.setTop(box.y+box.height);
36781 this.split.el.setWidth(box.width);
36783 if(this.collapsed){
36784 this.updateBody(box.width, null);
36786 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36794 Roo.bootstrap.layout.South = function(config){
36795 config.region = 'south';
36796 config.cursor = 's-resize';
36797 Roo.bootstrap.layout.Split.call(this, config);
36799 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36800 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36801 this.split.el.addClass("roo-layout-split-v");
36803 var size = config.initialSize || config.height;
36804 if(typeof size != "undefined"){
36805 this.el.setHeight(size);
36809 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36810 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36811 getBox : function(){
36812 if(this.collapsed){
36813 return this.collapsedEl.getBox();
36815 var box = this.el.getBox();
36817 var sh = this.split.el.getHeight();
36824 updateBox : function(box){
36825 if(this.split && !this.collapsed){
36826 var sh = this.split.el.getHeight();
36829 this.split.el.setLeft(box.x);
36830 this.split.el.setTop(box.y-sh);
36831 this.split.el.setWidth(box.width);
36833 if(this.collapsed){
36834 this.updateBody(box.width, null);
36836 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36840 Roo.bootstrap.layout.East = function(config){
36841 config.region = "east";
36842 config.cursor = "e-resize";
36843 Roo.bootstrap.layout.Split.call(this, config);
36845 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36846 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36847 this.split.el.addClass("roo-layout-split-h");
36849 var size = config.initialSize || config.width;
36850 if(typeof size != "undefined"){
36851 this.el.setWidth(size);
36854 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36855 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36856 getBox : function(){
36857 if(this.collapsed){
36858 return this.collapsedEl.getBox();
36860 var box = this.el.getBox();
36862 var sw = this.split.el.getWidth();
36869 updateBox : function(box){
36870 if(this.split && !this.collapsed){
36871 var sw = this.split.el.getWidth();
36873 this.split.el.setLeft(box.x);
36874 this.split.el.setTop(box.y);
36875 this.split.el.setHeight(box.height);
36878 if(this.collapsed){
36879 this.updateBody(null, box.height);
36881 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36885 Roo.bootstrap.layout.West = function(config){
36886 config.region = "west";
36887 config.cursor = "w-resize";
36889 Roo.bootstrap.layout.Split.call(this, config);
36891 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36892 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36893 this.split.el.addClass("roo-layout-split-h");
36897 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36898 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36900 onRender: function(ctr, pos)
36902 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36903 var size = this.config.initialSize || this.config.width;
36904 if(typeof size != "undefined"){
36905 this.el.setWidth(size);
36909 getBox : function(){
36910 if(this.collapsed){
36911 return this.collapsedEl.getBox();
36913 var box = this.el.getBox();
36915 box.width += this.split.el.getWidth();
36920 updateBox : function(box){
36921 if(this.split && !this.collapsed){
36922 var sw = this.split.el.getWidth();
36924 this.split.el.setLeft(box.x+box.width);
36925 this.split.el.setTop(box.y);
36926 this.split.el.setHeight(box.height);
36928 if(this.collapsed){
36929 this.updateBody(null, box.height);
36931 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36934 Roo.namespace("Roo.bootstrap.panel");/*
36936 * Ext JS Library 1.1.1
36937 * Copyright(c) 2006-2007, Ext JS, LLC.
36939 * Originally Released Under LGPL - original licence link has changed is not relivant.
36942 * <script type="text/javascript">
36945 * @class Roo.ContentPanel
36946 * @extends Roo.util.Observable
36947 * A basic ContentPanel element.
36948 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36949 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36950 * @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
36951 * @cfg {Boolean} closable True if the panel can be closed/removed
36952 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36953 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36954 * @cfg {Toolbar} toolbar A toolbar for this panel
36955 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36956 * @cfg {String} title The title for this panel
36957 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36958 * @cfg {String} url Calls {@link #setUrl} with this value
36959 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36960 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36961 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36962 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36963 * @cfg {Boolean} badges render the badges
36966 * Create a new ContentPanel.
36967 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36968 * @param {String/Object} config A string to set only the title or a config object
36969 * @param {String} content (optional) Set the HTML content for this panel
36970 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36972 Roo.bootstrap.panel.Content = function( config){
36974 this.tpl = config.tpl || false;
36976 var el = config.el;
36977 var content = config.content;
36979 if(config.autoCreate){ // xtype is available if this is called from factory
36982 this.el = Roo.get(el);
36983 if(!this.el && config && config.autoCreate){
36984 if(typeof config.autoCreate == "object"){
36985 if(!config.autoCreate.id){
36986 config.autoCreate.id = config.id||el;
36988 this.el = Roo.DomHelper.append(document.body,
36989 config.autoCreate, true);
36991 var elcfg = { tag: "div",
36992 cls: "roo-layout-inactive-content",
36996 elcfg.html = config.html;
37000 this.el = Roo.DomHelper.append(document.body, elcfg , true);
37003 this.closable = false;
37004 this.loaded = false;
37005 this.active = false;
37008 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37010 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37012 this.wrapEl = this.el; //this.el.wrap();
37014 if (config.toolbar.items) {
37015 ti = config.toolbar.items ;
37016 delete config.toolbar.items ;
37020 this.toolbar.render(this.wrapEl, 'before');
37021 for(var i =0;i < ti.length;i++) {
37022 // Roo.log(['add child', items[i]]);
37023 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37025 this.toolbar.items = nitems;
37026 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37027 delete config.toolbar;
37031 // xtype created footer. - not sure if will work as we normally have to render first..
37032 if (this.footer && !this.footer.el && this.footer.xtype) {
37033 if (!this.wrapEl) {
37034 this.wrapEl = this.el.wrap();
37037 this.footer.container = this.wrapEl.createChild();
37039 this.footer = Roo.factory(this.footer, Roo);
37044 if(typeof config == "string"){
37045 this.title = config;
37047 Roo.apply(this, config);
37051 this.resizeEl = Roo.get(this.resizeEl, true);
37053 this.resizeEl = this.el;
37055 // handle view.xtype
37063 * Fires when this panel is activated.
37064 * @param {Roo.ContentPanel} this
37068 * @event deactivate
37069 * Fires when this panel is activated.
37070 * @param {Roo.ContentPanel} this
37072 "deactivate" : true,
37076 * Fires when this panel is resized if fitToFrame is true.
37077 * @param {Roo.ContentPanel} this
37078 * @param {Number} width The width after any component adjustments
37079 * @param {Number} height The height after any component adjustments
37085 * Fires when this tab is created
37086 * @param {Roo.ContentPanel} this
37097 if(this.autoScroll){
37098 this.resizeEl.setStyle("overflow", "auto");
37100 // fix randome scrolling
37101 //this.el.on('scroll', function() {
37102 // Roo.log('fix random scolling');
37103 // this.scrollTo('top',0);
37106 content = content || this.content;
37108 this.setContent(content);
37110 if(config && config.url){
37111 this.setUrl(this.url, this.params, this.loadOnce);
37116 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37118 if (this.view && typeof(this.view.xtype) != 'undefined') {
37119 this.view.el = this.el.appendChild(document.createElement("div"));
37120 this.view = Roo.factory(this.view);
37121 this.view.render && this.view.render(false, '');
37125 this.fireEvent('render', this);
37128 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37132 setRegion : function(region){
37133 this.region = region;
37134 this.setActiveClass(region && !this.background);
37138 setActiveClass: function(state)
37141 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37142 this.el.setStyle('position','relative');
37144 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37145 this.el.setStyle('position', 'absolute');
37150 * Returns the toolbar for this Panel if one was configured.
37151 * @return {Roo.Toolbar}
37153 getToolbar : function(){
37154 return this.toolbar;
37157 setActiveState : function(active)
37159 this.active = active;
37160 this.setActiveClass(active);
37162 if(this.fireEvent("deactivate", this) === false){
37167 this.fireEvent("activate", this);
37171 * Updates this panel's element
37172 * @param {String} content The new content
37173 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37175 setContent : function(content, loadScripts){
37176 this.el.update(content, loadScripts);
37179 ignoreResize : function(w, h){
37180 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37183 this.lastSize = {width: w, height: h};
37188 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37189 * @return {Roo.UpdateManager} The UpdateManager
37191 getUpdateManager : function(){
37192 return this.el.getUpdateManager();
37195 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37196 * @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:
37199 url: "your-url.php",
37200 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37201 callback: yourFunction,
37202 scope: yourObject, //(optional scope)
37205 text: "Loading...",
37210 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37211 * 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.
37212 * @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}
37213 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37214 * @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.
37215 * @return {Roo.ContentPanel} this
37218 var um = this.el.getUpdateManager();
37219 um.update.apply(um, arguments);
37225 * 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.
37226 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37227 * @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)
37228 * @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)
37229 * @return {Roo.UpdateManager} The UpdateManager
37231 setUrl : function(url, params, loadOnce){
37232 if(this.refreshDelegate){
37233 this.removeListener("activate", this.refreshDelegate);
37235 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37236 this.on("activate", this.refreshDelegate);
37237 return this.el.getUpdateManager();
37240 _handleRefresh : function(url, params, loadOnce){
37241 if(!loadOnce || !this.loaded){
37242 var updater = this.el.getUpdateManager();
37243 updater.update(url, params, this._setLoaded.createDelegate(this));
37247 _setLoaded : function(){
37248 this.loaded = true;
37252 * Returns this panel's id
37255 getId : function(){
37260 * Returns this panel's element - used by regiosn to add.
37261 * @return {Roo.Element}
37263 getEl : function(){
37264 return this.wrapEl || this.el;
37269 adjustForComponents : function(width, height)
37271 //Roo.log('adjustForComponents ');
37272 if(this.resizeEl != this.el){
37273 width -= this.el.getFrameWidth('lr');
37274 height -= this.el.getFrameWidth('tb');
37277 var te = this.toolbar.getEl();
37278 te.setWidth(width);
37279 height -= te.getHeight();
37282 var te = this.footer.getEl();
37283 te.setWidth(width);
37284 height -= te.getHeight();
37288 if(this.adjustments){
37289 width += this.adjustments[0];
37290 height += this.adjustments[1];
37292 return {"width": width, "height": height};
37295 setSize : function(width, height){
37296 if(this.fitToFrame && !this.ignoreResize(width, height)){
37297 if(this.fitContainer && this.resizeEl != this.el){
37298 this.el.setSize(width, height);
37300 var size = this.adjustForComponents(width, height);
37301 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37302 this.fireEvent('resize', this, size.width, size.height);
37307 * Returns this panel's title
37310 getTitle : function(){
37312 if (typeof(this.title) != 'object') {
37317 for (var k in this.title) {
37318 if (!this.title.hasOwnProperty(k)) {
37322 if (k.indexOf('-') >= 0) {
37323 var s = k.split('-');
37324 for (var i = 0; i<s.length; i++) {
37325 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37328 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37335 * Set this panel's title
37336 * @param {String} title
37338 setTitle : function(title){
37339 this.title = title;
37341 this.region.updatePanelTitle(this, title);
37346 * Returns true is this panel was configured to be closable
37347 * @return {Boolean}
37349 isClosable : function(){
37350 return this.closable;
37353 beforeSlide : function(){
37355 this.resizeEl.clip();
37358 afterSlide : function(){
37360 this.resizeEl.unclip();
37364 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37365 * Will fail silently if the {@link #setUrl} method has not been called.
37366 * This does not activate the panel, just updates its content.
37368 refresh : function(){
37369 if(this.refreshDelegate){
37370 this.loaded = false;
37371 this.refreshDelegate();
37376 * Destroys this panel
37378 destroy : function(){
37379 this.el.removeAllListeners();
37380 var tempEl = document.createElement("span");
37381 tempEl.appendChild(this.el.dom);
37382 tempEl.innerHTML = "";
37388 * form - if the content panel contains a form - this is a reference to it.
37389 * @type {Roo.form.Form}
37393 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37394 * This contains a reference to it.
37400 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37410 * @param {Object} cfg Xtype definition of item to add.
37414 getChildContainer: function () {
37415 return this.getEl();
37420 var ret = new Roo.factory(cfg);
37425 if (cfg.xtype.match(/^Form$/)) {
37428 //if (this.footer) {
37429 // el = this.footer.container.insertSibling(false, 'before');
37431 el = this.el.createChild();
37434 this.form = new Roo.form.Form(cfg);
37437 if ( this.form.allItems.length) {
37438 this.form.render(el.dom);
37442 // should only have one of theses..
37443 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37444 // views.. should not be just added - used named prop 'view''
37446 cfg.el = this.el.appendChild(document.createElement("div"));
37449 var ret = new Roo.factory(cfg);
37451 ret.render && ret.render(false, ''); // render blank..
37461 * @class Roo.bootstrap.panel.Grid
37462 * @extends Roo.bootstrap.panel.Content
37464 * Create a new GridPanel.
37465 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37466 * @param {Object} config A the config object
37472 Roo.bootstrap.panel.Grid = function(config)
37476 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37477 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37479 config.el = this.wrapper;
37480 //this.el = this.wrapper;
37482 if (config.container) {
37483 // ctor'ed from a Border/panel.grid
37486 this.wrapper.setStyle("overflow", "hidden");
37487 this.wrapper.addClass('roo-grid-container');
37492 if(config.toolbar){
37493 var tool_el = this.wrapper.createChild();
37494 this.toolbar = Roo.factory(config.toolbar);
37496 if (config.toolbar.items) {
37497 ti = config.toolbar.items ;
37498 delete config.toolbar.items ;
37502 this.toolbar.render(tool_el);
37503 for(var i =0;i < ti.length;i++) {
37504 // Roo.log(['add child', items[i]]);
37505 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37507 this.toolbar.items = nitems;
37509 delete config.toolbar;
37512 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37513 config.grid.scrollBody = true;;
37514 config.grid.monitorWindowResize = false; // turn off autosizing
37515 config.grid.autoHeight = false;
37516 config.grid.autoWidth = false;
37518 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37520 if (config.background) {
37521 // render grid on panel activation (if panel background)
37522 this.on('activate', function(gp) {
37523 if (!gp.grid.rendered) {
37524 gp.grid.render(this.wrapper);
37525 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37530 this.grid.render(this.wrapper);
37531 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37534 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37535 // ??? needed ??? config.el = this.wrapper;
37540 // xtype created footer. - not sure if will work as we normally have to render first..
37541 if (this.footer && !this.footer.el && this.footer.xtype) {
37543 var ctr = this.grid.getView().getFooterPanel(true);
37544 this.footer.dataSource = this.grid.dataSource;
37545 this.footer = Roo.factory(this.footer, Roo);
37546 this.footer.render(ctr);
37556 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37557 getId : function(){
37558 return this.grid.id;
37562 * Returns the grid for this panel
37563 * @return {Roo.bootstrap.Table}
37565 getGrid : function(){
37569 setSize : function(width, height){
37570 if(!this.ignoreResize(width, height)){
37571 var grid = this.grid;
37572 var size = this.adjustForComponents(width, height);
37573 var gridel = grid.getGridEl();
37574 gridel.setSize(size.width, size.height);
37576 var thd = grid.getGridEl().select('thead',true).first();
37577 var tbd = grid.getGridEl().select('tbody', true).first();
37579 tbd.setSize(width, height - thd.getHeight());
37588 beforeSlide : function(){
37589 this.grid.getView().scroller.clip();
37592 afterSlide : function(){
37593 this.grid.getView().scroller.unclip();
37596 destroy : function(){
37597 this.grid.destroy();
37599 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37604 * @class Roo.bootstrap.panel.Nest
37605 * @extends Roo.bootstrap.panel.Content
37607 * Create a new Panel, that can contain a layout.Border.
37610 * @param {Roo.BorderLayout} layout The layout for this panel
37611 * @param {String/Object} config A string to set only the title or a config object
37613 Roo.bootstrap.panel.Nest = function(config)
37615 // construct with only one argument..
37616 /* FIXME - implement nicer consturctors
37617 if (layout.layout) {
37619 layout = config.layout;
37620 delete config.layout;
37622 if (layout.xtype && !layout.getEl) {
37623 // then layout needs constructing..
37624 layout = Roo.factory(layout, Roo);
37628 config.el = config.layout.getEl();
37630 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37632 config.layout.monitorWindowResize = false; // turn off autosizing
37633 this.layout = config.layout;
37634 this.layout.getEl().addClass("roo-layout-nested-layout");
37641 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37643 setSize : function(width, height){
37644 if(!this.ignoreResize(width, height)){
37645 var size = this.adjustForComponents(width, height);
37646 var el = this.layout.getEl();
37647 if (size.height < 1) {
37648 el.setWidth(size.width);
37650 el.setSize(size.width, size.height);
37652 var touch = el.dom.offsetWidth;
37653 this.layout.layout();
37654 // ie requires a double layout on the first pass
37655 if(Roo.isIE && !this.initialized){
37656 this.initialized = true;
37657 this.layout.layout();
37662 // activate all subpanels if not currently active..
37664 setActiveState : function(active){
37665 this.active = active;
37666 this.setActiveClass(active);
37669 this.fireEvent("deactivate", this);
37673 this.fireEvent("activate", this);
37674 // not sure if this should happen before or after..
37675 if (!this.layout) {
37676 return; // should not happen..
37679 for (var r in this.layout.regions) {
37680 reg = this.layout.getRegion(r);
37681 if (reg.getActivePanel()) {
37682 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37683 reg.setActivePanel(reg.getActivePanel());
37686 if (!reg.panels.length) {
37689 reg.showPanel(reg.getPanel(0));
37698 * Returns the nested BorderLayout for this panel
37699 * @return {Roo.BorderLayout}
37701 getLayout : function(){
37702 return this.layout;
37706 * Adds a xtype elements to the layout of the nested panel
37710 xtype : 'ContentPanel',
37717 xtype : 'NestedLayoutPanel',
37723 items : [ ... list of content panels or nested layout panels.. ]
37727 * @param {Object} cfg Xtype definition of item to add.
37729 addxtype : function(cfg) {
37730 return this.layout.addxtype(cfg);
37735 * Ext JS Library 1.1.1
37736 * Copyright(c) 2006-2007, Ext JS, LLC.
37738 * Originally Released Under LGPL - original licence link has changed is not relivant.
37741 * <script type="text/javascript">
37744 * @class Roo.TabPanel
37745 * @extends Roo.util.Observable
37746 * A lightweight tab container.
37750 // basic tabs 1, built from existing content
37751 var tabs = new Roo.TabPanel("tabs1");
37752 tabs.addTab("script", "View Script");
37753 tabs.addTab("markup", "View Markup");
37754 tabs.activate("script");
37756 // more advanced tabs, built from javascript
37757 var jtabs = new Roo.TabPanel("jtabs");
37758 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37760 // set up the UpdateManager
37761 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37762 var updater = tab2.getUpdateManager();
37763 updater.setDefaultUrl("ajax1.htm");
37764 tab2.on('activate', updater.refresh, updater, true);
37766 // Use setUrl for Ajax loading
37767 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37768 tab3.setUrl("ajax2.htm", null, true);
37771 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37774 jtabs.activate("jtabs-1");
37777 * Create a new TabPanel.
37778 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37779 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37781 Roo.bootstrap.panel.Tabs = function(config){
37783 * The container element for this TabPanel.
37784 * @type Roo.Element
37786 this.el = Roo.get(config.el);
37789 if(typeof config == "boolean"){
37790 this.tabPosition = config ? "bottom" : "top";
37792 Roo.apply(this, config);
37796 if(this.tabPosition == "bottom"){
37797 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37798 this.el.addClass("roo-tabs-bottom");
37800 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37801 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37802 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37804 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37806 if(this.tabPosition != "bottom"){
37807 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37808 * @type Roo.Element
37810 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37811 this.el.addClass("roo-tabs-top");
37815 this.bodyEl.setStyle("position", "relative");
37817 this.active = null;
37818 this.activateDelegate = this.activate.createDelegate(this);
37823 * Fires when the active tab changes
37824 * @param {Roo.TabPanel} this
37825 * @param {Roo.TabPanelItem} activePanel The new active tab
37829 * @event beforetabchange
37830 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37831 * @param {Roo.TabPanel} this
37832 * @param {Object} e Set cancel to true on this object to cancel the tab change
37833 * @param {Roo.TabPanelItem} tab The tab being changed to
37835 "beforetabchange" : true
37838 Roo.EventManager.onWindowResize(this.onResize, this);
37839 this.cpad = this.el.getPadding("lr");
37840 this.hiddenCount = 0;
37843 // toolbar on the tabbar support...
37844 if (this.toolbar) {
37845 alert("no toolbar support yet");
37846 this.toolbar = false;
37848 var tcfg = this.toolbar;
37849 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37850 this.toolbar = new Roo.Toolbar(tcfg);
37851 if (Roo.isSafari) {
37852 var tbl = tcfg.container.child('table', true);
37853 tbl.setAttribute('width', '100%');
37861 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37864 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37866 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37868 tabPosition : "top",
37870 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37872 currentTabWidth : 0,
37874 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37878 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37882 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37884 preferredTabWidth : 175,
37886 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37888 resizeTabs : false,
37890 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37892 monitorResize : true,
37894 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37899 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37900 * @param {String} id The id of the div to use <b>or create</b>
37901 * @param {String} text The text for the tab
37902 * @param {String} content (optional) Content to put in the TabPanelItem body
37903 * @param {Boolean} closable (optional) True to create a close icon on the tab
37904 * @return {Roo.TabPanelItem} The created TabPanelItem
37906 addTab : function(id, text, content, closable, tpl)
37908 var item = new Roo.bootstrap.panel.TabItem({
37912 closable : closable,
37915 this.addTabItem(item);
37917 item.setContent(content);
37923 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37924 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37925 * @return {Roo.TabPanelItem}
37927 getTab : function(id){
37928 return this.items[id];
37932 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37933 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37935 hideTab : function(id){
37936 var t = this.items[id];
37939 this.hiddenCount++;
37940 this.autoSizeTabs();
37945 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37946 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37948 unhideTab : function(id){
37949 var t = this.items[id];
37951 t.setHidden(false);
37952 this.hiddenCount--;
37953 this.autoSizeTabs();
37958 * Adds an existing {@link Roo.TabPanelItem}.
37959 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37961 addTabItem : function(item){
37962 this.items[item.id] = item;
37963 this.items.push(item);
37964 // if(this.resizeTabs){
37965 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37966 // this.autoSizeTabs();
37968 // item.autoSize();
37973 * Removes a {@link Roo.TabPanelItem}.
37974 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37976 removeTab : function(id){
37977 var items = this.items;
37978 var tab = items[id];
37979 if(!tab) { return; }
37980 var index = items.indexOf(tab);
37981 if(this.active == tab && items.length > 1){
37982 var newTab = this.getNextAvailable(index);
37987 this.stripEl.dom.removeChild(tab.pnode.dom);
37988 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37989 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37991 items.splice(index, 1);
37992 delete this.items[tab.id];
37993 tab.fireEvent("close", tab);
37994 tab.purgeListeners();
37995 this.autoSizeTabs();
37998 getNextAvailable : function(start){
37999 var items = this.items;
38001 // look for a next tab that will slide over to
38002 // replace the one being removed
38003 while(index < items.length){
38004 var item = items[++index];
38005 if(item && !item.isHidden()){
38009 // if one isn't found select the previous tab (on the left)
38012 var item = items[--index];
38013 if(item && !item.isHidden()){
38021 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38022 * @param {String/Number} id The id or index of the TabPanelItem to disable.
38024 disableTab : function(id){
38025 var tab = this.items[id];
38026 if(tab && this.active != tab){
38032 * Enables a {@link Roo.TabPanelItem} that is disabled.
38033 * @param {String/Number} id The id or index of the TabPanelItem to enable.
38035 enableTab : function(id){
38036 var tab = this.items[id];
38041 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38042 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38043 * @return {Roo.TabPanelItem} The TabPanelItem.
38045 activate : function(id){
38046 var tab = this.items[id];
38050 if(tab == this.active || tab.disabled){
38054 this.fireEvent("beforetabchange", this, e, tab);
38055 if(e.cancel !== true && !tab.disabled){
38057 this.active.hide();
38059 this.active = this.items[id];
38060 this.active.show();
38061 this.fireEvent("tabchange", this, this.active);
38067 * Gets the active {@link Roo.TabPanelItem}.
38068 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38070 getActiveTab : function(){
38071 return this.active;
38075 * Updates the tab body element to fit the height of the container element
38076 * for overflow scrolling
38077 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38079 syncHeight : function(targetHeight){
38080 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38081 var bm = this.bodyEl.getMargins();
38082 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38083 this.bodyEl.setHeight(newHeight);
38087 onResize : function(){
38088 if(this.monitorResize){
38089 this.autoSizeTabs();
38094 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38096 beginUpdate : function(){
38097 this.updating = true;
38101 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38103 endUpdate : function(){
38104 this.updating = false;
38105 this.autoSizeTabs();
38109 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38111 autoSizeTabs : function(){
38112 var count = this.items.length;
38113 var vcount = count - this.hiddenCount;
38114 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38117 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38118 var availWidth = Math.floor(w / vcount);
38119 var b = this.stripBody;
38120 if(b.getWidth() > w){
38121 var tabs = this.items;
38122 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38123 if(availWidth < this.minTabWidth){
38124 /*if(!this.sleft){ // incomplete scrolling code
38125 this.createScrollButtons();
38128 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38131 if(this.currentTabWidth < this.preferredTabWidth){
38132 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38138 * Returns the number of tabs in this TabPanel.
38141 getCount : function(){
38142 return this.items.length;
38146 * Resizes all the tabs to the passed width
38147 * @param {Number} The new width
38149 setTabWidth : function(width){
38150 this.currentTabWidth = width;
38151 for(var i = 0, len = this.items.length; i < len; i++) {
38152 if(!this.items[i].isHidden()) {
38153 this.items[i].setWidth(width);
38159 * Destroys this TabPanel
38160 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38162 destroy : function(removeEl){
38163 Roo.EventManager.removeResizeListener(this.onResize, this);
38164 for(var i = 0, len = this.items.length; i < len; i++){
38165 this.items[i].purgeListeners();
38167 if(removeEl === true){
38168 this.el.update("");
38173 createStrip : function(container)
38175 var strip = document.createElement("nav");
38176 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38177 container.appendChild(strip);
38181 createStripList : function(strip)
38183 // div wrapper for retard IE
38184 // returns the "tr" element.
38185 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38186 //'<div class="x-tabs-strip-wrap">'+
38187 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38188 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38189 return strip.firstChild; //.firstChild.firstChild.firstChild;
38191 createBody : function(container)
38193 var body = document.createElement("div");
38194 Roo.id(body, "tab-body");
38195 //Roo.fly(body).addClass("x-tabs-body");
38196 Roo.fly(body).addClass("tab-content");
38197 container.appendChild(body);
38200 createItemBody :function(bodyEl, id){
38201 var body = Roo.getDom(id);
38203 body = document.createElement("div");
38206 //Roo.fly(body).addClass("x-tabs-item-body");
38207 Roo.fly(body).addClass("tab-pane");
38208 bodyEl.insertBefore(body, bodyEl.firstChild);
38212 createStripElements : function(stripEl, text, closable, tpl)
38214 var td = document.createElement("li"); // was td..
38217 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38220 stripEl.appendChild(td);
38222 td.className = "x-tabs-closable";
38223 if(!this.closeTpl){
38224 this.closeTpl = new Roo.Template(
38225 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38226 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38227 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38230 var el = this.closeTpl.overwrite(td, {"text": text});
38231 var close = el.getElementsByTagName("div")[0];
38232 var inner = el.getElementsByTagName("em")[0];
38233 return {"el": el, "close": close, "inner": inner};
38236 // not sure what this is..
38237 // if(!this.tabTpl){
38238 //this.tabTpl = new Roo.Template(
38239 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38240 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38242 // this.tabTpl = new Roo.Template(
38243 // '<a href="#">' +
38244 // '<span unselectable="on"' +
38245 // (this.disableTooltips ? '' : ' title="{text}"') +
38246 // ' >{text}</span></a>'
38252 var template = tpl || this.tabTpl || false;
38256 template = new Roo.Template(
38258 '<span unselectable="on"' +
38259 (this.disableTooltips ? '' : ' title="{text}"') +
38260 ' >{text}</span></a>'
38264 switch (typeof(template)) {
38268 template = new Roo.Template(template);
38274 var el = template.overwrite(td, {"text": text});
38276 var inner = el.getElementsByTagName("span")[0];
38278 return {"el": el, "inner": inner};
38286 * @class Roo.TabPanelItem
38287 * @extends Roo.util.Observable
38288 * Represents an individual item (tab plus body) in a TabPanel.
38289 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38290 * @param {String} id The id of this TabPanelItem
38291 * @param {String} text The text for the tab of this TabPanelItem
38292 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38294 Roo.bootstrap.panel.TabItem = function(config){
38296 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38297 * @type Roo.TabPanel
38299 this.tabPanel = config.panel;
38301 * The id for this TabPanelItem
38304 this.id = config.id;
38306 this.disabled = false;
38308 this.text = config.text;
38310 this.loaded = false;
38311 this.closable = config.closable;
38314 * The body element for this TabPanelItem.
38315 * @type Roo.Element
38317 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38318 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38319 this.bodyEl.setStyle("display", "block");
38320 this.bodyEl.setStyle("zoom", "1");
38321 //this.hideAction();
38323 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38325 this.el = Roo.get(els.el);
38326 this.inner = Roo.get(els.inner, true);
38327 this.textEl = Roo.get(this.el.dom.firstChild, true);
38328 this.pnode = Roo.get(els.el.parentNode, true);
38329 // this.el.on("mousedown", this.onTabMouseDown, this);
38330 this.el.on("click", this.onTabClick, this);
38332 if(config.closable){
38333 var c = Roo.get(els.close, true);
38334 c.dom.title = this.closeText;
38335 c.addClassOnOver("close-over");
38336 c.on("click", this.closeClick, this);
38342 * Fires when this tab becomes the active tab.
38343 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38344 * @param {Roo.TabPanelItem} this
38348 * @event beforeclose
38349 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38350 * @param {Roo.TabPanelItem} this
38351 * @param {Object} e Set cancel to true on this object to cancel the close.
38353 "beforeclose": true,
38356 * Fires when this tab is closed.
38357 * @param {Roo.TabPanelItem} this
38361 * @event deactivate
38362 * Fires when this tab is no longer the active tab.
38363 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38364 * @param {Roo.TabPanelItem} this
38366 "deactivate" : true
38368 this.hidden = false;
38370 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38373 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38375 purgeListeners : function(){
38376 Roo.util.Observable.prototype.purgeListeners.call(this);
38377 this.el.removeAllListeners();
38380 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38383 this.pnode.addClass("active");
38386 this.tabPanel.stripWrap.repaint();
38388 this.fireEvent("activate", this.tabPanel, this);
38392 * Returns true if this tab is the active tab.
38393 * @return {Boolean}
38395 isActive : function(){
38396 return this.tabPanel.getActiveTab() == this;
38400 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38403 this.pnode.removeClass("active");
38405 this.fireEvent("deactivate", this.tabPanel, this);
38408 hideAction : function(){
38409 this.bodyEl.hide();
38410 this.bodyEl.setStyle("position", "absolute");
38411 this.bodyEl.setLeft("-20000px");
38412 this.bodyEl.setTop("-20000px");
38415 showAction : function(){
38416 this.bodyEl.setStyle("position", "relative");
38417 this.bodyEl.setTop("");
38418 this.bodyEl.setLeft("");
38419 this.bodyEl.show();
38423 * Set the tooltip for the tab.
38424 * @param {String} tooltip The tab's tooltip
38426 setTooltip : function(text){
38427 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38428 this.textEl.dom.qtip = text;
38429 this.textEl.dom.removeAttribute('title');
38431 this.textEl.dom.title = text;
38435 onTabClick : function(e){
38436 e.preventDefault();
38437 this.tabPanel.activate(this.id);
38440 onTabMouseDown : function(e){
38441 e.preventDefault();
38442 this.tabPanel.activate(this.id);
38445 getWidth : function(){
38446 return this.inner.getWidth();
38449 setWidth : function(width){
38450 var iwidth = width - this.pnode.getPadding("lr");
38451 this.inner.setWidth(iwidth);
38452 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38453 this.pnode.setWidth(width);
38457 * Show or hide the tab
38458 * @param {Boolean} hidden True to hide or false to show.
38460 setHidden : function(hidden){
38461 this.hidden = hidden;
38462 this.pnode.setStyle("display", hidden ? "none" : "");
38466 * Returns true if this tab is "hidden"
38467 * @return {Boolean}
38469 isHidden : function(){
38470 return this.hidden;
38474 * Returns the text for this tab
38477 getText : function(){
38481 autoSize : function(){
38482 //this.el.beginMeasure();
38483 this.textEl.setWidth(1);
38485 * #2804 [new] Tabs in Roojs
38486 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38488 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38489 //this.el.endMeasure();
38493 * Sets the text for the tab (Note: this also sets the tooltip text)
38494 * @param {String} text The tab's text and tooltip
38496 setText : function(text){
38498 this.textEl.update(text);
38499 this.setTooltip(text);
38500 //if(!this.tabPanel.resizeTabs){
38501 // this.autoSize();
38505 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38507 activate : function(){
38508 this.tabPanel.activate(this.id);
38512 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38514 disable : function(){
38515 if(this.tabPanel.active != this){
38516 this.disabled = true;
38517 this.pnode.addClass("disabled");
38522 * Enables this TabPanelItem if it was previously disabled.
38524 enable : function(){
38525 this.disabled = false;
38526 this.pnode.removeClass("disabled");
38530 * Sets the content for this TabPanelItem.
38531 * @param {String} content The content
38532 * @param {Boolean} loadScripts true to look for and load scripts
38534 setContent : function(content, loadScripts){
38535 this.bodyEl.update(content, loadScripts);
38539 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38540 * @return {Roo.UpdateManager} The UpdateManager
38542 getUpdateManager : function(){
38543 return this.bodyEl.getUpdateManager();
38547 * Set a URL to be used to load the content for this TabPanelItem.
38548 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38549 * @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)
38550 * @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)
38551 * @return {Roo.UpdateManager} The UpdateManager
38553 setUrl : function(url, params, loadOnce){
38554 if(this.refreshDelegate){
38555 this.un('activate', this.refreshDelegate);
38557 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38558 this.on("activate", this.refreshDelegate);
38559 return this.bodyEl.getUpdateManager();
38563 _handleRefresh : function(url, params, loadOnce){
38564 if(!loadOnce || !this.loaded){
38565 var updater = this.bodyEl.getUpdateManager();
38566 updater.update(url, params, this._setLoaded.createDelegate(this));
38571 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38572 * Will fail silently if the setUrl method has not been called.
38573 * This does not activate the panel, just updates its content.
38575 refresh : function(){
38576 if(this.refreshDelegate){
38577 this.loaded = false;
38578 this.refreshDelegate();
38583 _setLoaded : function(){
38584 this.loaded = true;
38588 closeClick : function(e){
38591 this.fireEvent("beforeclose", this, o);
38592 if(o.cancel !== true){
38593 this.tabPanel.removeTab(this.id);
38597 * The text displayed in the tooltip for the close icon.
38600 closeText : "Close this tab"
38603 * This script refer to:
38604 * Title: International Telephone Input
38605 * Author: Jack O'Connor
38606 * Code version: v12.1.12
38607 * Availability: https://github.com/jackocnr/intl-tel-input.git
38610 Roo.bootstrap.PhoneInputData = function() {
38613 "Afghanistan (افغانستان)",
38618 "Albania (Shqipëri)",
38623 "Algeria (الجزائر)",
38648 "Antigua and Barbuda",
38658 "Armenia (Հայաստան)",
38674 "Austria (Österreich)",
38679 "Azerbaijan (Azərbaycan)",
38689 "Bahrain (البحرين)",
38694 "Bangladesh (বাংলাদেশ)",
38704 "Belarus (Беларусь)",
38709 "Belgium (België)",
38739 "Bosnia and Herzegovina (Босна и Херцеговина)",
38754 "British Indian Ocean Territory",
38759 "British Virgin Islands",
38769 "Bulgaria (България)",
38779 "Burundi (Uburundi)",
38784 "Cambodia (កម្ពុជា)",
38789 "Cameroon (Cameroun)",
38798 ["204", "226", "236", "249", "250", "289", "306", "343", "365", "387", "403", "416", "418", "431", "437", "438", "450", "506", "514", "519", "548", "579", "581", "587", "604", "613", "639", "647", "672", "705", "709", "742", "778", "780", "782", "807", "819", "825", "867", "873", "902", "905"]
38801 "Cape Verde (Kabu Verdi)",
38806 "Caribbean Netherlands",
38817 "Central African Republic (République centrafricaine)",
38837 "Christmas Island",
38843 "Cocos (Keeling) Islands",
38854 "Comoros (جزر القمر)",
38859 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38864 "Congo (Republic) (Congo-Brazzaville)",
38884 "Croatia (Hrvatska)",
38905 "Czech Republic (Česká republika)",
38910 "Denmark (Danmark)",
38925 "Dominican Republic (República Dominicana)",
38929 ["809", "829", "849"]
38947 "Equatorial Guinea (Guinea Ecuatorial)",
38967 "Falkland Islands (Islas Malvinas)",
38972 "Faroe Islands (Føroyar)",
38993 "French Guiana (Guyane française)",
38998 "French Polynesia (Polynésie française)",
39013 "Georgia (საქართველო)",
39018 "Germany (Deutschland)",
39038 "Greenland (Kalaallit Nunaat)",
39075 "Guinea-Bissau (Guiné Bissau)",
39100 "Hungary (Magyarország)",
39105 "Iceland (Ísland)",
39125 "Iraq (العراق)",
39141 "Israel (ישראל)",
39168 "Jordan (الأردن)",
39173 "Kazakhstan (Казахстан)",
39194 "Kuwait (الكويت)",
39199 "Kyrgyzstan (Кыргызстан)",
39209 "Latvia (Latvija)",
39214 "Lebanon (لبنان)",
39229 "Libya (ليبيا)",
39239 "Lithuania (Lietuva)",
39254 "Macedonia (FYROM) (Македонија)",
39259 "Madagascar (Madagasikara)",
39289 "Marshall Islands",
39299 "Mauritania (موريتانيا)",
39304 "Mauritius (Moris)",
39325 "Moldova (Republica Moldova)",
39335 "Mongolia (Монгол)",
39340 "Montenegro (Crna Gora)",
39350 "Morocco (المغرب)",
39356 "Mozambique (Moçambique)",
39361 "Myanmar (Burma) (မြန်မာ)",
39366 "Namibia (Namibië)",
39381 "Netherlands (Nederland)",
39386 "New Caledonia (Nouvelle-Calédonie)",
39421 "North Korea (조선 민주주의 인민 공화국)",
39426 "Northern Mariana Islands",
39442 "Pakistan (پاکستان)",
39452 "Palestine (فلسطين)",
39462 "Papua New Guinea",
39504 "Réunion (La Réunion)",
39510 "Romania (România)",
39526 "Saint Barthélemy",
39537 "Saint Kitts and Nevis",
39547 "Saint Martin (Saint-Martin (partie française))",
39553 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39558 "Saint Vincent and the Grenadines",
39573 "São Tomé and Príncipe (São Tomé e Príncipe)",
39578 "Saudi Arabia (المملكة العربية السعودية)",
39583 "Senegal (Sénégal)",
39613 "Slovakia (Slovensko)",
39618 "Slovenia (Slovenija)",
39628 "Somalia (Soomaaliya)",
39638 "South Korea (대한민국)",
39643 "South Sudan (جنوب السودان)",
39653 "Sri Lanka (ශ්රී ලංකාව)",
39658 "Sudan (السودان)",
39668 "Svalbard and Jan Mayen",
39679 "Sweden (Sverige)",
39684 "Switzerland (Schweiz)",
39689 "Syria (سوريا)",
39734 "Trinidad and Tobago",
39739 "Tunisia (تونس)",
39744 "Turkey (Türkiye)",
39754 "Turks and Caicos Islands",
39764 "U.S. Virgin Islands",
39774 "Ukraine (Україна)",
39779 "United Arab Emirates (الإمارات العربية المتحدة)",
39801 "Uzbekistan (Oʻzbekiston)",
39811 "Vatican City (Città del Vaticano)",
39822 "Vietnam (Việt Nam)",
39827 "Wallis and Futuna (Wallis-et-Futuna)",
39832 "Western Sahara (الصحراء الغربية)",
39838 "Yemen (اليمن)",
39862 * This script refer to:
39863 * Title: International Telephone Input
39864 * Author: Jack O'Connor
39865 * Code version: v12.1.12
39866 * Availability: https://github.com/jackocnr/intl-tel-input.git
39870 * @class Roo.bootstrap.PhoneInput
39871 * @extends Roo.bootstrap.TriggerField
39872 * An input with International dial-code selection
39874 * @cfg {String} defaultDialCode default '+852'
39875 * @cfg {Array} preferedCountries default []
39878 * Create a new PhoneInput.
39879 * @param {Object} config Configuration options
39882 Roo.bootstrap.PhoneInput = function(config) {
39883 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39886 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39888 listWidth: undefined,
39890 selectedClass: 'active',
39892 invalidClass : "has-warning",
39894 validClass: 'has-success',
39896 allowed: '0123456789',
39901 * @cfg {String} defaultDialCode The default dial code when initializing the input
39903 defaultDialCode: '+852',
39906 * @cfg {Array} preferedCountries A list of iso2 in array (e.g. ['hk','us']). Those related countries will show at the top of the input's choices
39908 preferedCountries: false,
39910 getAutoCreate : function()
39912 var data = Roo.bootstrap.PhoneInputData();
39913 var align = this.labelAlign || this.parentLabelAlign();
39916 this.allCountries = [];
39917 this.dialCodeMapping = [];
39919 for (var i = 0; i < data.length; i++) {
39921 this.allCountries[i] = {
39925 priority: c[3] || 0,
39926 areaCodes: c[4] || null
39928 this.dialCodeMapping[c[2]] = {
39931 priority: c[3] || 0,
39932 areaCodes: c[4] || null
39944 // type: 'number', -- do not use number - we get the flaky up/down arrows.
39945 maxlength: this.max_length,
39946 cls : 'form-control tel-input',
39947 autocomplete: 'new-password'
39950 var hiddenInput = {
39953 cls: 'hidden-tel-input'
39957 hiddenInput.name = this.name;
39960 if (this.disabled) {
39961 input.disabled = true;
39964 var flag_container = {
39981 cls: this.hasFeedback ? 'has-feedback' : '',
39987 cls: 'dial-code-holder',
39994 cls: 'roo-select2-container input-group',
40001 if (this.fieldLabel.length) {
40004 tooltip: 'This field is required'
40010 cls: 'control-label',
40016 html: this.fieldLabel
40019 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40025 if(this.indicatorpos == 'right') {
40026 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40033 if(align == 'left') {
40041 if(this.labelWidth > 12){
40042 label.style = "width: " + this.labelWidth + 'px';
40044 if(this.labelWidth < 13 && this.labelmd == 0){
40045 this.labelmd = this.labelWidth;
40047 if(this.labellg > 0){
40048 label.cls += ' col-lg-' + this.labellg;
40049 input.cls += ' col-lg-' + (12 - this.labellg);
40051 if(this.labelmd > 0){
40052 label.cls += ' col-md-' + this.labelmd;
40053 container.cls += ' col-md-' + (12 - this.labelmd);
40055 if(this.labelsm > 0){
40056 label.cls += ' col-sm-' + this.labelsm;
40057 container.cls += ' col-sm-' + (12 - this.labelsm);
40059 if(this.labelxs > 0){
40060 label.cls += ' col-xs-' + this.labelxs;
40061 container.cls += ' col-xs-' + (12 - this.labelxs);
40071 var settings = this;
40073 ['xs','sm','md','lg'].map(function(size){
40074 if (settings[size]) {
40075 cfg.cls += ' col-' + size + '-' + settings[size];
40079 this.store = new Roo.data.Store({
40080 proxy : new Roo.data.MemoryProxy({}),
40081 reader : new Roo.data.JsonReader({
40092 'name' : 'dialCode',
40096 'name' : 'priority',
40100 'name' : 'areaCodes',
40107 if(!this.preferedCountries) {
40108 this.preferedCountries = [
40115 var p = this.preferedCountries.reverse();
40118 for (var i = 0; i < p.length; i++) {
40119 for (var j = 0; j < this.allCountries.length; j++) {
40120 if(this.allCountries[j].iso2 == p[i]) {
40121 var t = this.allCountries[j];
40122 this.allCountries.splice(j,1);
40123 this.allCountries.unshift(t);
40129 this.store.proxy.data = {
40131 data: this.allCountries
40137 initEvents : function()
40140 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40142 this.indicator = this.indicatorEl();
40143 this.flag = this.flagEl();
40144 this.dialCodeHolder = this.dialCodeHolderEl();
40146 this.trigger = this.el.select('div.flag-box',true).first();
40147 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40152 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40153 _this.list.setWidth(lw);
40156 this.list.on('mouseover', this.onViewOver, this);
40157 this.list.on('mousemove', this.onViewMove, this);
40158 this.inputEl().on("keyup", this.onKeyUp, this);
40159 this.inputEl().on("keypress", this.onKeyPress, this);
40161 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40163 this.view = new Roo.View(this.list, this.tpl, {
40164 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40167 this.view.on('click', this.onViewClick, this);
40168 this.setValue(this.defaultDialCode);
40171 onTriggerClick : function(e)
40173 Roo.log('trigger click');
40178 if(this.isExpanded()){
40180 this.hasFocus = false;
40182 this.store.load({});
40183 this.hasFocus = true;
40188 isExpanded : function()
40190 return this.list.isVisible();
40193 collapse : function()
40195 if(!this.isExpanded()){
40199 Roo.get(document).un('mousedown', this.collapseIf, this);
40200 Roo.get(document).un('mousewheel', this.collapseIf, this);
40201 this.fireEvent('collapse', this);
40205 expand : function()
40209 if(this.isExpanded() || !this.hasFocus){
40213 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40214 this.list.setWidth(lw);
40217 this.restrictHeight();
40219 Roo.get(document).on('mousedown', this.collapseIf, this);
40220 Roo.get(document).on('mousewheel', this.collapseIf, this);
40222 this.fireEvent('expand', this);
40225 restrictHeight : function()
40227 this.list.alignTo(this.inputEl(), this.listAlign);
40228 this.list.alignTo(this.inputEl(), this.listAlign);
40231 onViewOver : function(e, t)
40233 if(this.inKeyMode){
40236 var item = this.view.findItemFromChild(t);
40239 var index = this.view.indexOf(item);
40240 this.select(index, false);
40245 onViewClick : function(view, doFocus, el, e)
40247 var index = this.view.getSelectedIndexes()[0];
40249 var r = this.store.getAt(index);
40252 this.onSelect(r, index);
40254 if(doFocus !== false && !this.blockFocus){
40255 this.inputEl().focus();
40259 onViewMove : function(e, t)
40261 this.inKeyMode = false;
40264 select : function(index, scrollIntoView)
40266 this.selectedIndex = index;
40267 this.view.select(index);
40268 if(scrollIntoView !== false){
40269 var el = this.view.getNode(index);
40271 this.list.scrollChildIntoView(el, false);
40276 createList : function()
40278 this.list = Roo.get(document.body).createChild({
40280 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40281 style: 'display:none'
40284 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40287 collapseIf : function(e)
40289 var in_combo = e.within(this.el);
40290 var in_list = e.within(this.list);
40291 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40293 if (in_combo || in_list || is_list) {
40299 onSelect : function(record, index)
40301 if(this.fireEvent('beforeselect', this, record, index) !== false){
40303 this.setFlagClass(record.data.iso2);
40304 this.setDialCode(record.data.dialCode);
40305 this.hasFocus = false;
40307 this.fireEvent('select', this, record, index);
40311 flagEl : function()
40313 var flag = this.el.select('div.flag',true).first();
40320 dialCodeHolderEl : function()
40322 var d = this.el.select('input.dial-code-holder',true).first();
40329 setDialCode : function(v)
40331 this.dialCodeHolder.dom.value = '+'+v;
40334 setFlagClass : function(n)
40336 this.flag.dom.className = 'flag '+n;
40339 getValue : function()
40341 var v = this.inputEl().getValue();
40342 if(this.dialCodeHolder) {
40343 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40348 setValue : function(v)
40350 var d = this.getDialCode(v);
40352 //invalid dial code
40353 if(v.length == 0 || !d || d.length == 0) {
40355 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40356 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40362 this.setFlagClass(this.dialCodeMapping[d].iso2);
40363 this.setDialCode(d);
40364 this.inputEl().dom.value = v.replace('+'+d,'');
40365 this.hiddenEl().dom.value = this.getValue();
40370 getDialCode : function(v)
40374 if (v.length == 0) {
40375 return this.dialCodeHolder.dom.value;
40379 if (v.charAt(0) != "+") {
40382 var numericChars = "";
40383 for (var i = 1; i < v.length; i++) {
40384 var c = v.charAt(i);
40387 if (this.dialCodeMapping[numericChars]) {
40388 dialCode = v.substr(1, i);
40390 if (numericChars.length == 4) {
40400 this.setValue(this.defaultDialCode);
40404 hiddenEl : function()
40406 return this.el.select('input.hidden-tel-input',true).first();
40409 // after setting val
40410 onKeyUp : function(e){
40411 this.setValue(this.getValue());
40414 onKeyPress : function(e){
40415 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40422 * @class Roo.bootstrap.MoneyField
40423 * @extends Roo.bootstrap.ComboBox
40424 * Bootstrap MoneyField class
40427 * Create a new MoneyField.
40428 * @param {Object} config Configuration options
40431 Roo.bootstrap.MoneyField = function(config) {
40433 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40437 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40440 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40442 allowDecimals : true,
40444 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40446 decimalSeparator : ".",
40448 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40450 decimalPrecision : 0,
40452 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40454 allowNegative : true,
40456 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40460 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40462 minValue : Number.NEGATIVE_INFINITY,
40464 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40466 maxValue : Number.MAX_VALUE,
40468 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40470 minText : "The minimum value for this field is {0}",
40472 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40474 maxText : "The maximum value for this field is {0}",
40476 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40477 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40479 nanText : "{0} is not a valid number",
40481 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40485 * @cfg {String} defaults currency of the MoneyField
40486 * value should be in lkey
40488 defaultCurrency : false,
40490 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40492 thousandsDelimiter : false,
40494 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40505 getAutoCreate : function()
40507 var align = this.labelAlign || this.parentLabelAlign();
40519 cls : 'form-control roo-money-amount-input',
40520 autocomplete: 'new-password'
40523 var hiddenInput = {
40527 cls: 'hidden-number-input'
40530 if(this.max_length) {
40531 input.maxlength = this.max_length;
40535 hiddenInput.name = this.name;
40538 if (this.disabled) {
40539 input.disabled = true;
40542 var clg = 12 - this.inputlg;
40543 var cmd = 12 - this.inputmd;
40544 var csm = 12 - this.inputsm;
40545 var cxs = 12 - this.inputxs;
40549 cls : 'row roo-money-field',
40553 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40557 cls: 'roo-select2-container input-group',
40561 cls : 'form-control roo-money-currency-input',
40562 autocomplete: 'new-password',
40564 name : this.currencyName
40568 cls : 'input-group-addon',
40582 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40586 cls: this.hasFeedback ? 'has-feedback' : '',
40597 if (this.fieldLabel.length) {
40600 tooltip: 'This field is required'
40606 cls: 'control-label',
40612 html: this.fieldLabel
40615 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40621 if(this.indicatorpos == 'right') {
40622 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40629 if(align == 'left') {
40637 if(this.labelWidth > 12){
40638 label.style = "width: " + this.labelWidth + 'px';
40640 if(this.labelWidth < 13 && this.labelmd == 0){
40641 this.labelmd = this.labelWidth;
40643 if(this.labellg > 0){
40644 label.cls += ' col-lg-' + this.labellg;
40645 input.cls += ' col-lg-' + (12 - this.labellg);
40647 if(this.labelmd > 0){
40648 label.cls += ' col-md-' + this.labelmd;
40649 container.cls += ' col-md-' + (12 - this.labelmd);
40651 if(this.labelsm > 0){
40652 label.cls += ' col-sm-' + this.labelsm;
40653 container.cls += ' col-sm-' + (12 - this.labelsm);
40655 if(this.labelxs > 0){
40656 label.cls += ' col-xs-' + this.labelxs;
40657 container.cls += ' col-xs-' + (12 - this.labelxs);
40668 var settings = this;
40670 ['xs','sm','md','lg'].map(function(size){
40671 if (settings[size]) {
40672 cfg.cls += ' col-' + size + '-' + settings[size];
40679 initEvents : function()
40681 this.indicator = this.indicatorEl();
40683 this.initCurrencyEvent();
40685 this.initNumberEvent();
40688 initCurrencyEvent : function()
40691 throw "can not find store for combo";
40694 this.store = Roo.factory(this.store, Roo.data);
40695 this.store.parent = this;
40699 this.triggerEl = this.el.select('.input-group-addon', true).first();
40701 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40706 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40707 _this.list.setWidth(lw);
40710 this.list.on('mouseover', this.onViewOver, this);
40711 this.list.on('mousemove', this.onViewMove, this);
40712 this.list.on('scroll', this.onViewScroll, this);
40715 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40718 this.view = new Roo.View(this.list, this.tpl, {
40719 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40722 this.view.on('click', this.onViewClick, this);
40724 this.store.on('beforeload', this.onBeforeLoad, this);
40725 this.store.on('load', this.onLoad, this);
40726 this.store.on('loadexception', this.onLoadException, this);
40728 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40729 "up" : function(e){
40730 this.inKeyMode = true;
40734 "down" : function(e){
40735 if(!this.isExpanded()){
40736 this.onTriggerClick();
40738 this.inKeyMode = true;
40743 "enter" : function(e){
40746 if(this.fireEvent("specialkey", this, e)){
40747 this.onViewClick(false);
40753 "esc" : function(e){
40757 "tab" : function(e){
40760 if(this.fireEvent("specialkey", this, e)){
40761 this.onViewClick(false);
40769 doRelay : function(foo, bar, hname){
40770 if(hname == 'down' || this.scope.isExpanded()){
40771 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40779 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40783 initNumberEvent : function(e)
40785 this.inputEl().on("keydown" , this.fireKey, this);
40786 this.inputEl().on("focus", this.onFocus, this);
40787 this.inputEl().on("blur", this.onBlur, this);
40789 this.inputEl().relayEvent('keyup', this);
40791 if(this.indicator){
40792 this.indicator.addClass('invisible');
40795 this.originalValue = this.getValue();
40797 if(this.validationEvent == 'keyup'){
40798 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40799 this.inputEl().on('keyup', this.filterValidation, this);
40801 else if(this.validationEvent !== false){
40802 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40805 if(this.selectOnFocus){
40806 this.on("focus", this.preFocus, this);
40809 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40810 this.inputEl().on("keypress", this.filterKeys, this);
40812 this.inputEl().relayEvent('keypress', this);
40815 var allowed = "0123456789";
40817 if(this.allowDecimals){
40818 allowed += this.decimalSeparator;
40821 if(this.allowNegative){
40825 if(this.thousandsDelimiter) {
40829 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40831 var keyPress = function(e){
40833 var k = e.getKey();
40835 var c = e.getCharCode();
40838 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40839 allowed.indexOf(String.fromCharCode(c)) === -1
40845 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40849 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40854 this.inputEl().on("keypress", keyPress, this);
40858 onTriggerClick : function(e)
40865 this.loadNext = false;
40867 if(this.isExpanded()){
40872 this.hasFocus = true;
40874 if(this.triggerAction == 'all') {
40875 this.doQuery(this.allQuery, true);
40879 this.doQuery(this.getRawValue());
40882 getCurrency : function()
40884 var v = this.currencyEl().getValue();
40889 restrictHeight : function()
40891 this.list.alignTo(this.currencyEl(), this.listAlign);
40892 this.list.alignTo(this.currencyEl(), this.listAlign);
40895 onViewClick : function(view, doFocus, el, e)
40897 var index = this.view.getSelectedIndexes()[0];
40899 var r = this.store.getAt(index);
40902 this.onSelect(r, index);
40906 onSelect : function(record, index){
40908 if(this.fireEvent('beforeselect', this, record, index) !== false){
40910 this.setFromCurrencyData(index > -1 ? record.data : false);
40914 this.fireEvent('select', this, record, index);
40918 setFromCurrencyData : function(o)
40922 this.lastCurrency = o;
40924 if (this.currencyField) {
40925 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40927 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40930 this.lastSelectionText = currency;
40932 //setting default currency
40933 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40934 this.setCurrency(this.defaultCurrency);
40938 this.setCurrency(currency);
40941 setFromData : function(o)
40945 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40947 this.setFromCurrencyData(c);
40952 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40954 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40957 this.setValue(value);
40961 setCurrency : function(v)
40963 this.currencyValue = v;
40966 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40971 setValue : function(v)
40973 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40979 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40981 this.inputEl().dom.value = (v == '') ? '' :
40982 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40984 if(!this.allowZero && v === '0') {
40985 this.hiddenEl().dom.value = '';
40986 this.inputEl().dom.value = '';
40993 getRawValue : function()
40995 var v = this.inputEl().getValue();
41000 getValue : function()
41002 return this.fixPrecision(this.parseValue(this.getRawValue()));
41005 parseValue : function(value)
41007 if(this.thousandsDelimiter) {
41009 r = new RegExp(",", "g");
41010 value = value.replace(r, "");
41013 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41014 return isNaN(value) ? '' : value;
41018 fixPrecision : function(value)
41020 if(this.thousandsDelimiter) {
41022 r = new RegExp(",", "g");
41023 value = value.replace(r, "");
41026 var nan = isNaN(value);
41028 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41029 return nan ? '' : value;
41031 return parseFloat(value).toFixed(this.decimalPrecision);
41034 decimalPrecisionFcn : function(v)
41036 return Math.floor(v);
41039 validateValue : function(value)
41041 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41045 var num = this.parseValue(value);
41048 this.markInvalid(String.format(this.nanText, value));
41052 if(num < this.minValue){
41053 this.markInvalid(String.format(this.minText, this.minValue));
41057 if(num > this.maxValue){
41058 this.markInvalid(String.format(this.maxText, this.maxValue));
41065 validate : function()
41067 if(this.disabled || this.allowBlank){
41072 var currency = this.getCurrency();
41074 if(this.validateValue(this.getRawValue()) && currency.length){
41079 this.markInvalid();
41083 getName: function()
41088 beforeBlur : function()
41094 var v = this.parseValue(this.getRawValue());
41101 onBlur : function()
41105 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41106 //this.el.removeClass(this.focusClass);
41109 this.hasFocus = false;
41111 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41115 var v = this.getValue();
41117 if(String(v) !== String(this.startValue)){
41118 this.fireEvent('change', this, v, this.startValue);
41121 this.fireEvent("blur", this);
41124 inputEl : function()
41126 return this.el.select('.roo-money-amount-input', true).first();
41129 currencyEl : function()
41131 return this.el.select('.roo-money-currency-input', true).first();
41134 hiddenEl : function()
41136 return this.el.select('input.hidden-number-input',true).first();