User:Thundercraft5/global.js/Ace.js/hoverlink.js

/** Original source: https://jsbin.com/jehopaja/4/edit?html,output **/ /* jshint esversion: 6, forin: true, immed: true, indent: 4, latedef: true, newcap: true, noarg: true, undef: true, undef: true, unused: true, browser: true, jquery: true, onevar: true, eqeqeq: true, multistr: true, maxerr: 99999, -W082, -W084, /* global ace, mw */ /** * Test */ ace.define("ace/ext/hoverlink", [], function(require, exports) {	"use strict";	mw.util.addCSS(`.ace_link_marker { position: absolute; margin-top: -2px; border-radius: unset !important; border-bottom: 1.5px solid currentcolor; background: rgba(255, 255, 255, 0.05); transistion: background 0.5s; }`);	function replace(s, searcher, replaceValue) {		return s.replace(searcher, typeof(replaceValue) === 'function' ? function(match, ...args) { const [offset, origStr, groups] = args.slice(-3); args = args.slice(0, -3); return replaceValue.call(null, {				match,				offset,				origStr,				groups,				orderedGroups: args,			}); } : replaceValue);	}	var oop = require("ace/lib/oop");	var event = require("ace/lib/event");	var Range = require("ace/range").Range;	var EventEmitter = require("ace/lib/event_emitter").EventEmitter;	const fullLinkRegex = /^\[\[(? (?:(?:(? [\w\.\-]+):)?(? (?:(?!.*(?:~{3,}))[^\n<>\[\]\{\}\|])+?)))(?:\|(?[^\0]*?))?\]\]$/;	const shortLinkRegex = /\[\[((?!.*(?:~{3,}))[^\n<>\[\]\{\}\|])+?(?:\|([^\0]*?))?\]\]/;	function createClassList(tokens) {		return tokens.split(".").map(v => "ace_"+v).join(" ");	}	function wLinkToTitle(link) {		return link.replace(/^\[{2}(.*?)(?:\|.*?)?\]{2}$/, '$1');	}	var HoverLink = function(editor, interwikimap=[]) {		if (editor.hoverLink)			return;		editor.hoverLink = this;		this.editor = editor;		this.interwikimap = interwikimap;		this.config = mw.config.get([ 'wgServer', 'wgArticlePath', ]);		this.config.wgArticlePath = this.config.wgArticlePath.replace('$1', '');		this.update = this.update.bind(this);		this.onMouseMove = this.onMouseMove.bind(this);		this.onMouseOut = this.onMouseOut.bind(this);		this.onClick = this.onClick.bind(this);		event.addListener(editor.renderer.scroller, "mousemove", this.onMouseMove);		event.addListener(editor.renderer.content, "mouseout", this.onMouseOut);		event.addListener(editor.renderer.content, "click", this.onClick);	};	(function { oop.implement(this, EventEmitter); this.token = {}; this.range = new Range; this.update = function { this.$timer = null; var editor = this.editor; var renderer = editor.renderer; var canvasPos = renderer.scroller.getBoundingClientRect; var offset = (this.x + renderer.scrollLeft - canvasPos.left - renderer.$padding) / renderer.characterWidth; var row = Math.floor((this.y + renderer.scrollTop - canvasPos.top) / renderer.lineHeight); var col = Math.round(offset); var screenPos = {row: row, column: col, side: offset - col > 0 ? 1 : -1};			var session = editor.session; var docPos = session.screenToDocumentPosition(screenPos.row, screenPos.column); var selectionRange = editor.selection.getRange; if (!selectionRange.isEmpty) { if (selectionRange.start.row <= row && selectionRange.end.row >= row) return this.clear; }			var line = editor.session.getLine(docPos.row); if (docPos.column === line.length) { var clippedPos = editor.session.documentToScreenPosition(docPos.row, docPos.column); if (clippedPos.column !== screenPos.column) { return this.clear; }			}			var token = this.findLink(docPos.row, docPos.column); this.link = token; if (!token) { return this.clear; }			this.isOpen = true; editor.renderer.setCursorStyle("pointer"); session.removeMarker(this.marker); this.removeEditorLink; this.range = new Range(token.row, token.start, token.row, token.start + token.value.length); this.marker = session.addMarker(this.range, "ace_link_marker " + token.tokens, "text", true); this.setEditorLink(token.value, token.title); };		this.setEditorLink = function(href, title) { this.editor.container.setAttribute('title', title); this.editor.container.setAttribute('href', href); };		this.removeEditorLink = function { this.editor.container.removeAttribute('title'); this.editor.container.removeAttribute('href'); };		this.clear = function { if (this.isOpen) { this.editor.session.removeMarker(this.marker); this.editor.renderer.setCursorStyle(""); this.isOpen = false; this.removeEditorLink; }		};		this.getMatchAround = function(regExp, string, col) { var match; regExp.lastIndex = 0; string.replace(regExp, function(str) {				var offset = arguments[arguments.length-2];				var length = str.length;				if (offset <= col && offset + length >= col)					match = {						start: offset,						value: str,					};			}); return match; };		this.onClick = function { if (this.link) { this.link.editor = this.editor; this._signal("open", this.link); this.clear; }		};		this.findLink = function(row, column) { var editor = this.editor; var session = editor.session; var line = session.getLine(row); var extLinkMatch = this.getMatchAround(/\w+:\/\/((?!(?<![\{\[\(][^\s]*)([\}\]\)]))[^\s"'`>])+/g, line, column);			var intLinkMatch = this.getMatchAround(shortLinkRegex, line, column);			var match = extLinkMatch || intLinkMatch;			if (!match) return;			const firstToken = editor.session.getTokenAt(row, match.start).type;			if (!this.scanLinkTokens(firstToken, row, match.start, match.value.length)) return;			match.row = row;			match.type = extLinkMatch ? 'external' : 'local';			match.title = wLinkToTitle(match.value);			match.value = match.type === 'local' ? this.createLink(match.value) : match.value;			match.tokens = createClassList(firstToken);			return match;		};		this.onMouseMove = function(e) {			if (this.editor.$mouseHandler.isMousePressed) {				if (!this.editor.selection.isEmpty)					this.clear;				return;			}			this.x = e.clientX;			this.y = e.clientY;			this.update;		};		this.onMouseOut = function { this.clear; };		this.destroy = function { this.onMouseOut; event.removeListener(this.editor.renderer.scroller, "mousemove", this.onMouseMove); event.removeListener(this.editor.renderer.content, "mouseout", this.onMouseOut); delete this.editor.hoverLink; };		this.createLink = function(pageText) { return replace(pageText, fullLinkRegex, ({ groups: { pagename, path, prefix }}) => {				var prefixedLink = this.interwikimap.find(link => link.prefix === prefix);				if (prefix === 'w' && pagename.slice(0, 2) === 'c:') {					let [fullWiki, ...linkPath] = pagename.slice(2).split(":");					linkPath = linkPath.join(':');					const [lang, wiki=fullWiki] = fullWiki.split('.');					return `https://${wiki}.fandom.com/${ lang === wiki ? "" : lang + "/" }wiki/${linkPath}`;					} else if (prefixedLink) {					return prefixedLink.url.replace('$1', prefix);				} else {					return this.config.wgServer + this.config.wgArticlePath + path;				}			}); };		this.scanLinkTokens = function(startToken, row, start, end) { for (let col = start; col++ < end;) { if (this.editor.session.getTokenAt(row, col).type !== startToken) return false; }			return true; };	}).call(HoverLink.prototype);	exports.HoverLink = HoverLink; }); new mw.Api.get({	action: "query",	meta: "siteinfo",	siprop: "interwikimap", }).then(({ query: { interwikimap }}) => {	var HoverLink = ace.require("ace/ext/hoverlink").HoverLink;	ace.editor.hoverLink = new HoverLink(ace.editor, interwikimap);	ace.editor.hoverLink.on("open", function(linkObj) { open(linkObj.value); }); });