2,733
edits
Red Leader (talk | contribs) m (Cleaup comment + regex simplification) |
Red Leader (talk | contribs) (Re-design of the script. It does the same job, just differently and more generically. Please test and give feedback via #Issues.2Flimitations, etc. P.S. I will comment things, etc later.) |
||
Line 72: | Line 72: | ||
<syntaxhighlight lang="javascript" enclose="div"> | <syntaxhighlight lang="javascript" enclose="div"> | ||
// ==UserScript== | // ==UserScript== | ||
// @name | // @name Instant-Cquotes | ||
// @ | // @version 0.18 | ||
// @description Automatically converts mailing list and forum quotes into Mediawiki markup. | // @description Automatically converts mailing list and forum quotes into Mediawiki markup. | ||
// @ | // @icon http://wiki.flightgear.org/images/6/62/FlightGear_logo.png | ||
// @ | // @match http://sourceforge.net/p/flightgear/mailman/*", | ||
// @ | // @match http://forum.flightgear.org/* | ||
// @require | // @namespace http://wiki.flightgear.org/ | ||
// @require | // @run-at document-end | ||
// @resource | // @require http://code.jquery.com/jquery-2.1.4.min.js | ||
// @grant | // @require http://code.jquery.com/ui/1.11.4/jquery-ui.min.js | ||
// @grant | // @resource jQUI_CSS http://code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css | ||
// @grant GM_addStyle | |||
// @grant GM_getResourceText | |||
// ==/UserScript== | // ==/UserScript== | ||
try { | |||
GM_addStyle(GM_getResourceText("jQUI_CSS")); | |||
} | |||
catch(error){} | |||
"use strict"; | |||
// Define constants | |||
var DEBUG = false; | |||
var CONFIG = { | var CONFIG = { | ||
"sourceforge.net": { | "sourceforge.net": { | ||
name: "mailing list", | |||
content: { | content: { | ||
selection: getSelectedText, | selection: getSelectedText, | ||
transform: newline2br | idStyle: /msg[0-9]{8}/, | ||
parentTag: ["tagName", "PRE"], | |||
transform: [newline2br] | |||
}, | }, | ||
author: { | author: { | ||
xpath: " | xpath: "tbody/tr[1]/td/div/small/text()", | ||
transform: extract(/From: (.*) <.*@.*>/) | transform: extract(/From: (.*) <.*@.*>/) | ||
}, | }, | ||
title: { | title: { | ||
xpath: " | xpath: "tbody/tr[1]/td/div/div[1]/b/a/text()" | ||
}, | }, | ||
date: { | date: { | ||
xpath: " | xpath: "tbody/tr[1]/td/div/small/text()", | ||
transform: extract(/- (.*-.*-.*) /) | transform: extract(/- (.*-.*-.*) /) | ||
}, | }, | ||
url: { | url: { | ||
xpath: " | xpath: "tbody/tr[1]/td/div/div[1]/b/a/@href", | ||
transform: prepend("http://sourceforge.net") | transform: prepend("http://sourceforge.net") | ||
} | } | ||
}, | }, | ||
"forum.flightgear.org": { | "forum.flightgear.org": { | ||
name: "forum", | |||
content: { | content: { | ||
selection: getSelectedHtml, | selection: getSelectedHtml, | ||
transform: [removeComments | idStyle: /p[0-9]{6}/, | ||
parentTag: ["className", "content", "postbody"], | |||
transform: [removeComments, escapePipes, a2wikilink, phpBB_smilies2text, | |||
img2link, phpBB_fontstyle2wikistyle, phpBB_code2syntaxhighlight, | |||
img2link | phpBB_quote2cquote, vid2wiki, escapeEquals, addNewlines] | ||
phpBB_quote2cquote | |||
}, | }, | ||
author: { | author: { | ||
xpath: " | xpath: "div/div[1]/p/strong/a/text()" | ||
}, | }, | ||
title: { | title: { | ||
xpath: " | xpath: "div/div[1]/h3/a/text()" | ||
}, | }, | ||
date: { | date: { | ||
xpath: " | xpath: "div/div[1]/p/text()[2]", | ||
transform: extract(/» (.* | transform: extract(/» (.*?[0-9]{4})/) | ||
}, | }, | ||
url: { | url: { | ||
xpath: " | xpath: "div/div[1]/p/a/@href", | ||
transform: [extract(/\.(.*)/), | transform: [extract(/\.(.*)/), prepend("http://forum.flightgear.org")] | ||
} | } | ||
} | } | ||
}; | }; | ||
var METHODS = { | |||
// Shows a window.prompt() message box | |||
msgbox: function(msg) { | |||
window.prompt("Copy to clipboard", msg); | |||
}, | |||
jQueryDiag: function(msg) { | |||
var diagDiv = $('<div id="MyDialog"><textarea id="quotedtext" rows="10"cols="80" style="width: 290px; height: 290px">' + msg + '</textarea></div>'); | |||
var diagParam = { | |||
title: "Copy your quote with Ctrl+c", | |||
modal: true, | |||
width: 'auto', | |||
buttons: [ | |||
{ text: "Select all", click: function() { $('#quotedtext').select(); } }, | |||
{ text: "OK", click: function() { $(this).dialog('close'); } } | |||
] | |||
}; | |||
diagDiv.dialog(diagParam); | |||
} | |||
}; | }; | ||
var OUTPUT = METHODS.msgbox; | |||
var MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; | |||
// ################## | |||
// # Main functions # | |||
// ################## | |||
window.addEventListener('load', init); | |||
function init() { | |||
document.onmouseup = instantCquote; | |||
dbLog("init(): Instant-Cquotes script initialized and ready to use"); | |||
} | } | ||
function | function instantCquote() { | ||
var profile = CONFIG[window.location.hostname], | |||
selection = document.getSelection(), | |||
output = {}, | |||
field = {}; | |||
try { | |||
var post_id = getPostId(selection, profile); | |||
} | |||
catch (error) { | |||
return; | |||
} | |||
if (selection.toString() === "") { | |||
dbLog("instantCquote(): No text is selected, aborting function"); | |||
return; | |||
function | |||
return | |||
} | } | ||
if (!checkValid(selection, profile)) { | |||
dbLog("instantCquote(): Selection is not valid, aborting function"); | |||
return; | |||
return | |||
} | } | ||
for (field in profile) { | |||
if (field === "name") continue; | |||
var fieldData = extractFieldInfo(profile, post_id, field); | |||
var transform = profile[field].transform; | |||
if (transform !== undefined) { | |||
dbLog("instantCquote(): Field '" + field + "' before transformation:\n'" + fieldData + "'"); | |||
fieldData = applyTransformations(fieldData, transform); | |||
dbLog("instantCquote(): Field '" + field + "' after transformation:\n'" + fieldData + "'"); | |||
} | |||
output[field] = fieldData; | |||
} | } | ||
output = createCquote(output); | |||
OUTPUT(output); | |||
} | } | ||
// | function getSelectedHtml() { | ||
// | // From http://stackoverflow.com/a/6668159 | ||
var html = "", | |||
selection = document.getSelection(); | |||
if (selection.rangeCount) { | |||
var container = document.createElement('div'); | |||
for (var i = 0; i < selection.rangeCount; i++) { | |||
container.appendChild(selection.getRangeAt(i).cloneContents()); | |||
} | |||
html = container.innerHTML; | |||
} | } | ||
dbLog("instantCquote(): Unprocessed HTML\n'" + html + "'"); | |||
return html; | |||
} | } | ||
// IE8 compat. function | |||
function getSelectedText() { | |||
return document.getSelection().toString(); | |||
function | |||
} | } | ||
function getPostId(selection, profile, focus) { | |||
if (focus !== undefined) { | |||
if( | selection = selection.focusNode.parentNode; | ||
} else { | } else { | ||
selection = selection.anchorNode.parentNode; | |||
} | |||
while (selection.id.match(profile.content.idStyle) === null) { | |||
selection = selection.parentNode; | |||
} | } | ||
return selection.id; | |||
} | } | ||
function | function checkValid(selection, profile) { | ||
var | var ret = true, | ||
selection_cp = {}, | |||
tags = profile.content.parentTag; | |||
for (var n = 0; n < 2; n++) { | |||
if (n === 0) { | |||
selection_cp = selection.anchorNode.parentNode; | |||
} | } else { | ||
selection_cp = selection.focusNode.parentNode; | |||
} | |||
while(true) { | |||
if (selection_cp.tagName === "BODY") { | |||
ret = false; | |||
break; | |||
} else { | |||
var cont = false; | |||
for (var i = 0; i < tags.length; i++) { | |||
if (selection_cp[tags[0]] === tags[i]) { | |||
cont = true; | |||
break; | |||
} | |||
} | |||
if (cont) { | |||
break; | |||
} else { | |||
selection_cp = selection_cp.parentNode; | |||
} | |||
} | |||
} | |||
} | } | ||
return | ret = ret && (getPostId(selection, profile) === getPostId(selection, profile, 1)); | ||
return ret; | |||
} | } | ||
function extractFieldInfo(profile, | function extractFieldInfo(profile, id, field) { | ||
if (field | if (field === "content") { | ||
return profile[field].selection(); | |||
} else { | } else { | ||
var xpath = '//*[@id="' + id + '"]/' + profile[field].xpath; | |||
return document.evaluate(xpath, document, null, XPathResult.STRING_TYPE, null).stringValue; | |||
} | } | ||
} | } | ||
function applyTransformations(fieldInfo, trans) { | function applyTransformations(fieldInfo, trans) { | ||
if(typeof trans === "function") { | if (typeof trans === "function") { | ||
dbLog(fieldInfo); | |||
return trans(fieldInfo); | return trans(fieldInfo); | ||
} else if (Array.isArray(trans)) { | } else if (Array.isArray(trans)) { | ||
for (var i = 0; i < trans.length; i++) { | for (var i = 0; i < trans.length; i++) { | ||
fieldInfo = trans[i](fieldInfo); | fieldInfo = trans[i](fieldInfo); | ||
dbLog("applyTransformations(): Multiple transformation, transformation after loop #" + (i + 1) + ":\n'" + fieldInfo + "'"); | |||
} | } | ||
return fieldInfo; | return fieldInfo; | ||
Line 427: | Line 330: | ||
} | } | ||
function createCquote( | function createCquote(data) { | ||
var wikiText = "{{FGCquote\n" + | |||
var | "|" + data.content + "\n" + | ||
"|" + | "| {{cite web\n" + | ||
"|{{cite web | " | url = " + data.url + "\n" + | ||
" | title = " + nowiki(data.title) + "\n" + | |||
" |title = " + nowiki( | " | author = " + nowiki(data.author) + "\n" + | ||
" |author = " + nowiki( | " | date = " + datef(data.date) + "\n" + | ||
" |date = " + datef( | " }}\n" + | ||
" }} | |||
"}}"; | "}}"; | ||
return | |||
return wikiText; | |||
} | |||
// ############# | |||
// # Utilities # | |||
// ############# | |||
function extract(regex) { | |||
return function(text) { | |||
return text.match(regex)[1]; | |||
}; | |||
} | |||
function prepend(prefix) { | |||
return function(text) { | |||
return prefix + text; | |||
}; | |||
} | |||
function removeComments(html) { | |||
return html.replace(/<!--.*?-->/g, ""); | |||
} | |||
function escapePipes(text) { | |||
text = text.replace(/\|\|/g,"{{!!}}"); | |||
text = text.replace(/\|\-/g,"{{!-}}"); | |||
return text.replace(/\|/g,"{{!}}"); | |||
} | |||
// Converts HTML <a href="...">...</a> tags to wiki links, internal if possible. | |||
function a2wikilink(html){ | |||
// Links to wiki images, because | |||
// they need special treatment, or else they get displayed. | |||
html = html.replace(/<a.*?href="http:\/\/wiki\.flightgear\.org\/File:(.*?)".*?>(.*?)<\/a>/g, "[[Media:$1|$2]]"); | |||
// Wiki links without custom text. | |||
html = html.replace(/<a.*?href="http:\/\/wiki\.flightgear\.org\/(.*?)".*?>http:\/\/wiki\.flightgear\.org\/.*?<\/a>/g, "[[$1]]"); | |||
// Links to the wiki with custom text | |||
html = html.replace(/<a.*?href="http:\/\/wiki\.flightgear\.org\/(.*?)".*?>(.*?)<\/a>/g, "[[$1|$2]]"); | |||
// Remove underscores from all wiki links | |||
var list = html.match(/\[\[.*?\]\]/g); | |||
if(list !== null){ | |||
for(var i = 0; i < list.length; i++){ | |||
html = html.replace(list[i], underscore2Space(list[i])); | |||
} | |||
} | |||
// Convert non-wiki links | |||
html = html.replace(/<a.*?href="(.*?)".*?>(.*?)<\/a>/g, "[$1 $2]"); | |||
// Remove triple dots from external links. | |||
// Replace with raw URL (MediaWiki converts it to a link). | |||
list = html.match(/\[.*?(\.\.\.).*?\]/g); | |||
if(list !== null){ | |||
for(var i = 0; i < list.length; i++){ | |||
html = html.replace(list[i], list[i].match(/\[(.*?) .*?\]/)[1]); | |||
} | |||
} | |||
return html; | |||
} | |||
function img2link(html) { | |||
html = html.replace(/<img.*?src="http:\/\/wiki\.flightgear\.org\/images\/.*?\/.*?\/(.*?)".*?>/g, "[[File:$1|250px]]"); | |||
return html.replace(/<img.*?src="(.*?)".*?>/g, "(see the [$1 linked image])"); | |||
} | |||
function phpBB_smilies2text(html) { | |||
return html.replace(/<img src="\.\/images\/smilies\/icon_.*?\.gif" alt="(.*?)".*?>/g, "$1"); | |||
} | |||
function phpBB_fontstyle2wikistyle(html) { | |||
html = html.replace(/<span style="font-weight: bold">(.*?)<\/span>/g, "'''$1'''"); | |||
html = html.replace(/<span style="text-decoration: underline">(.*?)<\/span>/g, "<u>$1</u>"); | |||
html = html.replace(/<span style="font-style: italic">(.*?)<\/span>/g, "''$1''"); | |||
return html.replace(/<span class="posthilit">(.*?)<\/span>/g, "$1"); | |||
} | } | ||
function | function phpBB_code2syntaxhighlight(html) { | ||
/ | return html.replace(/<dl.*?><dt>.*?<\/dt><dd><code>(.*?)<\/code><\/dd><\/dl>/g, '<pre style="white-space:pre-wrap">\n$1\n</pre>'); | ||
} | } | ||
function phpBB_quote2cquote(html) { | |||
return html.replace(/<blockquote.*?><div>(?:<cite>.*?<\/cite>)?(.*?)<\/div><\/blockquote>/g, "{{cquote|$1}}"); | |||
} | |||
function vid2wiki(html){ | |||
// YouTube | |||
html = html.replace(/<div.*?><iframe width="(.*?)" height="(.*?)" src="http:\/\/www\.youtube\.com\/embed\/(.*?)".*?><\/iframe><\/div>/g, "{{#ev:youtube|$3|$1x$2}}"); | |||
html = html.replace(/\[.*? Watch on Youtube\]/g, ""); | |||
// Vimeo: Doesn't currently work (cannot select video viewer on post) | |||
html = html.replace(/<iframe src="http:\/\/player\.vimeo\.com\/video\/(.*?)\?.*?" width="(.*?)" height="(.*?)".*?>.*?<\/iframe>/g, "{{#ev:vimeo|$3|$1x$2}}"); | |||
return html.replace(/\[.*? Watch on Vimeo\]/g, ""); | |||
} | |||
function escapeEquals(html) { | |||
return html.replace(/=/g,"{{=}}"); | |||
} | |||
function addNewlines(html) { | |||
html = html.replace(/<br\/?>/g, "<br/>\n"); | |||
html = html.replace(/(<\/?[uo]l>)/g, "$1\n"); | |||
return html.replace(/<\/li>/g, "</li>\n"); | |||
} | |||
function nowiki(text) { | function nowiki(text) { | ||
return "<nowiki>" + text + "</nowiki>"; | return "<nowiki>" + text + "</nowiki>"; | ||
} | } | ||
// Returns the correct ordinal adjective | // Returns the correct ordinal adjective | ||
function ordAdj(date){ | function ordAdj(date){ | ||
date = date.toString(); | |||
if(date == "11" || date == "12" || date == "13"){ | |||
return "th"; | |||
}else if(date.substr(1) == "1" || date == "1"){ | |||
return "st"; | |||
}else if(date.substr(1) == "2" || date == "2"){ | |||
return "nd"; | |||
}else if(date.substr(1) == "3" || date == "3"){ | |||
return "rd"; | |||
}else{ | |||
return "th"; | |||
} | |||
}; | }; | ||
// Formats the date to this format: Apr 26th, 2015 | // Formats the date to this format: Apr 26th, 2015 | ||
function datef(text){ | function datef(text){ | ||
var date = new Date(text); | |||
return MONTHS[date.getMonth()] + " " + date.getDate() + ordAdj(date.getDate()) + ", " + date.getFullYear(); | |||
} | } | ||
function underscore2Space(str){ | function underscore2Space(str){ | ||
return str.replace(/_/g, " "); | |||
} | } | ||
function newline2br(text) { | |||
function | return text.replace(/\n/g, "<br/>\n"); | ||
} | } | ||
function dbLog(message) { | |||
function | if (Boolean(DEBUG)) console.log(message); | ||
if( | |||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
[[Category:Wiki maintenance]] | [[Category:Wiki maintenance]] |