User:V111P/js/msgDisplay.js

/* * msgDisplay.js * ver. 2013-12-02 * Home: http://en.wikipedia.org/wiki/User:V111P/js/msgDisplay * * This script can be used by other scripts to display messages to the MediaWiki editor. * To get the default display, call msgDisplay without parameters. * To get another display, call msgDisplay(displayID). * See the bottom of the file to see which methods you can call * on the object returned by msgDisplay. * * CC0 Public Domain Dedication: * http://creativecommons.org/publicdomain/zero/1.0/ * If you use large parts of this code, please let me know. * You should also let others know where the code originates: * http://en.wikipedia.org/wiki/User:V111P/js/msgDisplay.js * Thanks. */

mediaWiki.libs.msgDisplay = mediaWiki.libs.msgDisplay || function (displayId) { "use strict"; var smd = mediaWiki.libs.msgDisplay; var displayId = displayId || 'top'; var d = smd.displays[displayId];

if (!d) { d = smd.displays[displayId] = createDisplay(displayId); }

return d;

function createDisplay(displayId) { "use strict";

function prt(str) { if (console && console.log) console.log(str); }

var domId = 'msgDisplay_' + displayId; var ltr = (document.getElementsByTagName('html')[0].dir != 'rtl');

var miscStyle = { emSizeInPx: 12, // to be set in show minDisplayHeight: '1.4em', // user config name: minHeight displayDivExpandHeight: '11.5em', // user config name: expandHeight // the enhanced editing toolbar has z-index:5, the (X) link in the siteNotice has 98 displayDivCollapsedZindex: 7, displayDivExpandedZindex: 101, innerResizeHandleHeightPx: 3, floatingMenuPxFromBottom: 19 // updated according to emSizeInPx in show };

var commonsUrl = '//upload.wikimedia.org/wikipedia/commons/';

var elProps = { staticParentDiv: { id: domId + '_outerBox', 'class': 'msgDisplay_outerBox', css: { height: miscStyle.minDisplayHeight, 'margin-left': (ltr ? '0' : '-13px'), 'margin-right': (ltr ? '-13px' : '0'), 'padding-bottom': '2px', // to make up for displayDiv's border 'background-color': '#ddd' }			},			displayDiv: { id: domId, 'class': 'msgDisplay', tabindex: 0, css: { height: miscStyle.minDisplayHeight, // original, collapsed height width: '100%', overflow: 'auto', 'overflow-x': 'hidden', position: 'absolute', 'z-index': miscStyle.displayDivCollapsedZindex, border: 'black solid 1px', background: '#fed', // can be set by user through msgDisplayConfid[displayId].background 'line-height': 'normal' // to cancel .wikiEditor-ui .wikiEditor-ui-text {line-height: 0} }			},			floatingMenu: { 'class': 'msgDisplay_floatingMenu', css: { position: 'absolute', left: (ltr ? 'auto' : 0), right: (ltr ? 0 : 'auto'), cursor: 'pointer', margin: '1px 2px', background: 'inherit' }			},			floatingMenuExpandedSection: { 'class': 'msgDisplay_expandedSection' },			floatingMenuButtonImg: { 'class': 'msgDisplay_menuButton', src: commonsUrl + 'thumb/8/8f/WMF-Agora-Settings_BCBCBC.svg/26px-WMF-Agora-Settings_BCBCBC.svg.png', width: 13, height: 13, alt: '[*]', title: '[*]', css: {cursor: 'auto'} },			okButtonImg: { 'class': 'msgDisplay_okButton', src: commonsUrl + 'thumb/a/ac/Approve_icon.svg/26px-Approve_icon.svg.png', width: 13, height: 13, alt: '[_]', title: '[_]' },			upButtonImg: { 'class': 'msgDisplay_upButton', src: commonsUrl + '5/56/Icon_Arrow_Up_26x26.png', width: 13, height: 13, alt: '[^]', title: '[^]' },			helpLink: { 'class': 'msgDisplay_helpLink', href: helpLinkUrl, // reset in addTheFloatingMenu target: '_blank', title: '?', tabindex: -1 },			helpImg: { src: commonsUrl + 'thumb/9/9e/Blue_Question.svg/26px-Blue_Question.svg.png', width: 13, height: 13, alt: '[?]', title: '[?]' },			closeButtonImg: { 'class': 'msgDisplay_closeButton', src: commonsUrl + 'thumb/d/d3/Button_hover.svg/26px-Button_hover.svg.png', width: 13, height: 13, alt: '[X]', title: '[X]', tabindex: -1 }		};

var defaultHelpLinkUrl = '//en.wikipedia.org/wiki/User:V111P/js/msgDisplay/Help'; var helpLinkUrl = defaultHelpLinkUrl;

var defaultLocations = { // 'content' means mw.util.$content top: {insertRelTo: 'content', insertRel: 'prepend'}, edit: {insertRelTo: '#wpTextbox1', insertRel: 'before'} };		var defaultExpandCollapseChar = '!'; var defaultBlurChar = '`'; var defaultCloseChar = '^'; var keyPressFns = []; // use keypress to register a keypress event handler var onCloseOnUserActionFns = []; // use onCloseOnUserAction to register an event handler var onCollapseOnUserActionFns = []; // use onCollapseOnUserAction to register an event handler

var $staticParentDiv; var $displayDiv, $innerResizeHandle; var $buffer = $(' '); var $menuDiv; var $floatingMenu;

var configAll = window.msgDisplayConfig = window.msgDisplayConfig || {}; var cConfig = configAll._common || {}; var dConfig = configAll[displayId] || {};

var styleTagClass = 'msgDisplay_style'; $('style.' + styleTagClass).remove; $(document.head).append(''			+ 'div.msgDisplay'				+ ' .msgDisplay_floatingMenu .msgDisplay_expandedSection {display: none;}\n'			+ 'div.msgDisplay'				+ ' .msgDisplay_floatingMenu:hover .msgDisplay_expandedSection {display: inline;}\n'			+ 'div.msgDisplay .msgDisplay_floatingMenu '				+ 'img {width: 1em; height: 1em; cursor: pointer; margin: 0px 1px;}\n'		);

function config(configObj) { if (!configObj) return;

var propNames = ['closeChar', 'expandCollapseChar', 'blurChar', 'height', 'minHeight', 'expandHeight', 'background', 'insertRelTo', 'insertRel'];

$.each(propNames, function (i, name) {				dConfig[name] = configObj[name] || dConfig[name];			});

return this; }

function remove { $(window).off('resize.' + domId); $displayDiv = null; clearBuffer; $('#' + elProps.staticParentDiv.id).remove; return this; }

// called from show only function setConfig { miscStyle.minDisplayHeight = dConfig.minHeight; // if undefd, will be calculated in show if (dConfig.height) elProps.staticParentDiv.css.height = elProps.displayDiv.css.height = dConfig.height; if (dConfig.expandHeight) miscStyle.displayDivExpandHeight = dConfig.expandHeight; var backgr = dConfig.background || cConfig.background; if (backgr) elProps.displayDiv.css.background = backgr;

var insertRelTo; if (dConfig.insertRelTo == 'content') ; // set below else if (dConfig.insertRelTo && $(dConfig.insertRelTo)[0]) insertRelTo = dConfig.insertRelTo; else if (displayId == 'edit') insertRelTo = defaultLocations['edit'].insertRelTo; else // displayId == 'top' or displayId == something else insertRelTo = defaultLocations['top'].insertRelTo;

if (insertRelTo == 'content') insertRelTo = mw.util.$content;

var insertRel; if (dConfig.insertRel) insertRel = dConfig.insertRel; else if (displayId == 'edit' || insertRelTo == '#wpTextbox1') insertRel = defaultLocations['edit'].insertRel; else // displayId == 'top' or displayId == something else insertRel = defaultLocations['top'].insertRel;

return {insertRelTo: insertRelTo, insertRel: insertRel}; } // setConfig

function show { clear; var pos = setConfig;

if (displayId == 'top' || displayId == 'edit') { helpLinkUrl = defaultHelpLinkUrl; removeAllEventHandlers; }			if ($displayDiv) { // already shown $displayDiv.css({					height: elProps.displayDiv.css.height,					background: elProps.displayDiv.css.background				}); $staticParentDiv.css({					height: elProps.staticParentDiv.css.height				}); return this; }

$displayDiv = $(' ', elProps.displayDiv) .dblclick(onDblClickHandler) .keypress(keyPressed);

var $div = $staticParentDiv = $(' ', elProps.staticParentDiv).append($displayDiv);

var insertRelTo = pos.insertRelTo; var insertRel = pos.insertRel;

// Insert relative to WikEd if ((displayId == 'edit' || insertRelTo == '#wpTextbox1') && window.wikEd) { var $wikEdContainer; if (insertRel == 'after') { $wikEdContainer = $('#wikEdConsoleWrapper'); if ($wikEdContainer[0]) { $div.prependTo($wikEdContainer); insertRel = 'wikEd'; }				}				else { $wikEdContainer = $('#wikEdCaptchaWrapper'); if ($wikEdContainer[0]) { $div.appendTo($wikEdContainer); insertRel = 'wikEd'; }				}			}

switch (insertRel) { case 'wikEd': // already inserted break; case 'prepend': $div.prependTo(insertRelTo); break; case 'append': $div.appendTo(insertRelTo); break; case 'after': if (window.syntaxHighlighterConfig					 && insertRelTo == '#wpTextbox1') { // support for the Dot's syntax highlighter gadget $div.insertAfter($('#wpTextbox1')[0].parentNode); }				else $div.insertAfter(insertRelTo); break; case 'before': default: if (window.syntaxHighlighterConfig					 && insertRelTo == '#wpTextbox1') { // support for the Dot's syntax highlighter gadget $div.insertBefore($('#wpTextbox1')[0].parentNode); }				else $div.insertBefore(insertRelTo); break; }

function resizeIt(e) { if (e && e.target != window) return; if (e) $div.css('width', 'auto'); var outerDivWidth = $div.width; $displayDiv.width(outerDivWidth - 15); }

resizeIt; $(window).on('resize.' + domId, resizeIt);

addTheFloatingMenu;

if (typeof miscStyle.minDisplayHeight != 'number') miscStyle.minDisplayHeight = $displayDiv.height; makeStaticParentDivResizable(miscStyle.minDisplayHeight); makeInnerDivResizable;

return this; } // show

// privete fn		function onDblClickHandler(e) { // remove the selection which resulted from the double click if (window.getSelection) { var selection = window.getSelection; if (selection.empty) selection.empty; else if (selection.removeAllRanges) selection.removeAllRanges; else if (selection.createRange && selection.createRange.collapse) { var r = selection.createRange; (r.collapse) && collapse(false); // IE before version 9 (?) }			}

expandCollapse(void(0), true); } // onDblClickHandler

// private fn		function makeInnerDivResizable(makeNotCancel) { if (!$.ui.resizable) return; if (makeNotCancel === false) { // cancel it				$displayDiv.off('scroll.resize').off('resizestop'); $displayDiv.resizable('destroy'); $innerResizeHandle = void(0); }			else { // turn it on				$displayDiv.resizable({ minHeight: $staticParentDiv.height, minWidth: 30, handles: 's' }); $innerResizeHandle = $displayDiv.find('div.ui-resizable-handle'); $innerResizeHandle.css('height', miscStyle.innerResizeHandleHeightPx + 'px'); $displayDiv.on('scroll.resize', function {positionInnerResizeHandleAndMenu;}) .on('resizestart', function(evt, ui) {						$floatingMenu.hide;						putOnTopOfOtherDisplays;					}) .on('resizestop', function(evt, ui) {positionInnerResizeHandleAndMenu;} ); positionInnerResizeHandleAndMenu; }		} // makeInnerDivResizable

// private fn		// move the innerResizeHandle and the floating menu to the bottom of the display function positionInnerResizeHandleAndMenu { var bottom = $displayDiv.height + $displayDiv.scrollTop; $innerResizeHandle.css('top', (bottom - miscStyle.innerResizeHandleHeightPx) + 'px'); $floatingMenu.show.css('top', (bottom - miscStyle.floatingMenuPxFromBottom) + 'px'); }

// private fn		function putOnTopOfOtherDisplays { $.each(smd.displays, function (i, val) {				val.collapse;			}); $displayDiv.css('z-index', miscStyle.displayDivExpandedZindex); }

// private fn		function makeStaticParentDivResizable(minHeight) { if (!$staticParentDiv.resizable) return; // For jQueryUI bug on resizing horizontally on right-to-left wikis: var left, width, ddLeft, ddWidth; $staticParentDiv.resizable({				minHeight: minHeight,				minWidth: 30,				handles: (ltr ? 'se' : 'sw'),				alsoResize: $displayDiv			}) .on('resizestart', function(e, ui) {				if (!ltr)					left = $staticParentDiv.css('left');				$floatingMenu.hide;			}) .on('resizestop', function(e, ui) {				e.stopPropagation;				if (!ltr) // fix jQueryUI's bug, which moves the div on resizing it					$staticParentDiv.css({left: left});				$displayDiv.css('width', $staticParentDiv.width - 15)				.resizable('option', 'minHeight', $staticParentDiv.height);				positionInnerResizeHandleAndMenu;			}) } // makeStaticParentDivResizable

// private fn		function addTheFloatingMenu { var $menuButtonImg; var $okButtonImg; var $upButtonImg; var $closeButtonImg; var $expandedSection;

if ($floatingMenu) { // $menuButtonImg = $floatingMenu.find('.' + elProps.floatingMenuButtonImg['class']); $okButtonImg = $floatingMenu.find('.' + elProps.okButtonImg['class']); $upButtonImg = $floatingMenu.find('.' + elProps.upButtonImg['class']); $closeButtonImg = $floatingMenu.find('.' + elProps.closeButtonImg['class']); // $expandedSection = $floatingMenu.find('.' + elProps.floatingMenuExpandedSection['class']); $floatingMenu.find('.' + elProps.helpLink['class']).attr('href', helpLinkUrl); }			else { $menuButtonImg = $(' ', elProps.floatingMenuButtonImg); $okButtonImg = $(' ', elProps.okButtonImg); $upButtonImg = $(' ', elProps.upButtonImg); $closeButtonImg = $(' ', elProps.closeButtonImg); $expandedSection = $(' ', elProps.floatingMenuExpandedSection);

$expandedSection.append($closeButtonImg) .append($('', elProps.helpLink).attr('href', helpLinkUrl)					.append($(' ', elProps.helpImg))) .append($upButtonImg) .append($okButtonImg);

$floatingMenu = $(' ', elProps.floatingMenu) .append($expandedSection) .append($menuButtonImg); }

$okButtonImg.click(function (e) {				expandCollapse(false, true);			}); $upButtonImg.click(function (e) {				scrollTo(0);			}); $closeButtonImg.click(closeOnUserAction);

$displayDiv.append($floatingMenu);

miscStyle.floatingMenuPxFromBottom = $floatingMenu.outerHeight(true) + 1; } // addTheFloatingMenu

function isShown { return ($displayDiv ? true : false); }

function collapse { expandCollapse(false); return this; }

function expand { expandCollapse(true); return this; }

function expandCollapse(expand, runOnCollapseEvtHandlers) { if (!$displayDiv) return; if (expand == null || expand == undefined) expand = ($staticParentDiv.height + 3 >= $displayDiv.height);

if (expand) { var preHeight = $displayDiv.height; var minExpandH = $staticParentDiv.height; putOnTopOfOtherDisplays; $displayDiv.css('height', miscStyle.displayDivExpandHeight); var aftHeight = $displayDiv.height; if (aftHeight < minExpandH) $displayDiv.height(minExpandH); positionInnerResizeHandleAndMenu; }			else { // collapse $displayDiv.css('z-index', miscStyle.displayDivCollapsedZindex); $displayDiv.height($staticParentDiv.height); $displayDiv.width($staticParentDiv.width - 15); positionInnerResizeHandleAndMenu; if (runOnCollapseEvtHandlers) { if (displayId == 'edit') focusTextbox1; $.each(onCollapseOnUserActionFns, function (i, fn) {						fn;					}); }			}

return this; } // expandCollapseDisplay

function focusTextbox1 { var scroll = $(window).scrollTop; $('#wpTextbox1').focus; $(window).scrollTop(scroll); // focus causes IE to scroll the page }

// private fn		function keyPressed(e) { var processKeyPress = true; $.each(keyPressFns, function (i, fn) {				if (processKeyPress)					processKeyPress = (fn(e) === false ? false : true);				else					fn(e);			}); if (!processKeyPress) // if at least one fn returned 'false', return return;

var charCode = e.which || e.charCode || e.keyCode; var character = String.fromCharCode(charCode);

var blurChar = dConfig.blurChar || cConfig.blurChar || defaultBlurChar; var expandCollapseChar = dConfig.expandCollapseChar || cConfig.expandCollapseChar || defaultExpandCollapseChar; var closeChar = dConfig.closeChar || cConfig.closeChar || defaultCloseChar;

if ((charCode == 27 && !e.shiftKey) || character === blurChar) { e.preventDefault; e.stopPropagation; expandCollapse(false, true); }			else if (character === expandCollapseChar) { e.preventDefault; e.stopPropagation; expandCollapse(void(0), true); }			else if (charCode == 27 || character === closeChar) { // 27 is Esc e.preventDefault; e.stopPropagation; closeOnUserAction; }		} // keyPressed

// register an event handler on the event 'keypress'. // If fn returns false, the default keys for closing, collapsing, etc. won't have any effect. function keypress(fn) { if ($.inArray(fn, keyPressFns) == -1) keyPressFns.push(fn); return this; }

// on clicking on the 'close' button in the floating menu // if fn returns false, the display won't close function onCloseOnUserAction(fn) { if ($.inArray(fn, onCloseOnUserActionFns) == -1) onCloseOnUserActionFns.push(fn); return this; }

function closeOnUserAction { var close = true; $.each(onCloseOnUserActionFns, function (i, fn) {				if (close) // if one of the fns returns 'false', don't close the display					close = (fn === false ? false : true);				else					fn;			}); if (close) { remove; if (displayId == 'edit') focusTextbox1; }		}

// on double clicking to collapse the display function onCollapseOnUserAction(fn) { if ($.inArray(fn, onCollapseOnUserActionFns) == -1) onCollapseOnUserActionFns.push(fn); return this; }

function removeAllEventHandlers { keyPressFns = []; onCloseOnUserAction = []; onCollapseOnUserActionFns = []; }

function helpUrl(url) { helpLinkUrl = url; if ($displayDiv) $displayDiv.find('.' + elProps.helpLink['class']).attr('href', url); return this; }

function append(htmlOrJqueryObj) { $buffer.append(htmlOrJqueryObj); return this; }

function appendText(text) { $buffer.append(document.createTextNode(text)); return this; }

// private fn		function clearBuffer { $buffer = $(' '); }

function write { if ($displayDiv) { $displayDiv.append($buffer.contents); clearBuffer; }			return this; }

function appendWrite(htmlOrJqueryObj) { append(htmlOrJqueryObj); if ($displayDiv) write; return this; }

function appendTextWrite(text) { append(document.createTextNode(text)); if ($displayDiv) write; return this; }

function clear { clearBuffer; if ($displayDiv) { makeInnerDivResizable(false); $displayDiv.html(''); addTheFloatingMenu; scrollTo(0); makeInnerDivResizable; }			return this; } // clear

function find(jQSelector) { if (!$displayDiv) return $([]); write; return $displayDiv.find(jQSelector); }

function focus(elOrSelector) { if (!$displayDiv) return this; if (typeof elOrSelector != 'undefined') { var $el; if (typeof elOrSelector == 'string') $el = find(elOrSelector); else // assume an HTML DOM node or a jQuery collection $el = $(elOrSelector); var scroll = $(window).scrollTop; $el.focus; $(window).scrollTop(scroll); // focus causes IE to scroll to top of page }			return this; } // focus

// to: y-coordinate, jQuery selector (string), or html node or jQuery object // focusEl: true - focus the element (if 1st agr is not just a y-coordinate) function scrollTo(to, focusEl) { if (!$displayDiv) return this; var $el; if (typeof to == 'number') { $displayDiv.scrollTop(to); }			else { if (typeof to == 'string') $el = find(to); else // assume to is an HTML node or a jQuery collection $el = $(to); if ($el[0]) { if (focusEl) focus($el); $displayDiv.scrollTop($displayDiv.scrollTop + $el.position.top) }			}

return this; } // scrollTo

return { show: show, remove: remove, isShown: isShown, expand: expand, collapse: collapse, expandCollapse: expandCollapse, keypress: keypress, helpUrl: helpUrl, onCloseOnUserAction: onCloseOnUserAction, onCollapseOnUserAction: onCollapseOnUserAction, removeAllEventHandlers: removeAllEventHandlers, config: config, clear: clear, append: append, appendText: appendText, write: write, // flushes the append/appendText buffer appendWrite: appendWrite, // flushes the append/appendText buffer appendTextWrite: appendTextWrite, // flushes the append/appendText buffer focus: focus, scrollTo: scrollTo, find: find, // Searches for and returns a jQuery collection. Flushes the append/appendText buffer focusTextbox1: focusTextbox1 }	}; // createDisplay

};

if (!mediaWiki.libs.msgDisplay.displays) { mediaWiki.libs.msgDisplay.displays = {}; if (!$.ui || !$.ui.resizable) mw.loader.load('jquery.ui'); }

mediaWiki.libs.msgDisplay.version = 1000;