20,741
edits
m (oops) |
m (→The Script: jQueryDiag: prepare hooks for changing the output/target format dynamically (placeholder for now)) |
||
| Line 101: | Line 101: | ||
// @noframes | // @noframes | ||
// ==/UserScript== | // ==/UserScript== | ||
// Check if Greasemonkey/Tampermonkey is available | // Check if Greasemonkey/Tampermonkey is available | ||
try { | try { | ||
GM_addStyle(GM_getResourceText('jQUI_CSS')); | |||
} | |||
catch (error) { | |||
} | } | ||
'use strict'; | |||
// Define constants | // Define constants | ||
var DEBUG = false; | var DEBUG = false; | ||
var CONFIG = { | var CONFIG = { | ||
'Mailing list': { | |||
url_reg: /http:\/\/sourceforge\.net\/p\/flightgear\/mailman\/.*/, | |||
content: { | |||
selection: getSelectedText, | |||
idStyle: /msg[0-9]{8}/, | |||
parentTag: [ | |||
'tagName', | |||
'PRE' | |||
] | |||
}, | |||
author: { | |||
xpath: 'tbody/tr[1]/td/div/small/text()', | |||
transform: extract(/From: (.*) <.*@.*>/) | |||
}, | |||
title: { | |||
xpath: 'tbody/tr[1]/td/div/div[1]/b/a/text()' | |||
}, | |||
date: { | |||
xpath: 'tbody/tr[1]/td/div/small/text()', | |||
transform: extract(/- (.*-.*-.*) /) | |||
}, | |||
} | url: { | ||
xpath: 'tbody/tr[1]/td/div/div[1]/b/a/@href', | |||
transform: prepend('http://sourceforge.net') | |||
} | |||
}, | |||
'FlightGear forum': { | |||
url_reg: /http:\/\/forum\.flightgear\.org\/.*/, | |||
content: { | |||
selection: getSelectedHtml, | |||
idStyle: /p[0-9]{6}/, | |||
parentTag: [ | |||
'className', | |||
'content', | |||
'postbody' | |||
], | |||
transform: [ | |||
removeComments, | |||
forum_quote2cquote, | |||
forum_smilies2text, | |||
forum_fontstyle2wikistyle, | |||
forum_code2syntaxhighlight, | |||
img2link, | |||
a2wikilink, | |||
vid2wiki, | |||
list2wiki, | |||
forum_br2newline | |||
] | |||
}, | |||
author: { | |||
xpath: 'div/div[1]/p/strong/a/text()' | |||
}, | |||
title: { | |||
xpath: 'div/div[1]/h3/a/text()' | |||
}, | |||
date: { | |||
xpath: 'div/div[1]/p/text()[2]', | |||
transform: extract(/» (.*?[0-9]{4})/) | |||
}, | }, | ||
url: { | |||
xpath: 'div/div[1]/p/a/@href', | |||
transform: [ | |||
extract(/\.(.*)/), | |||
prepend('http://forum.flightgear.org') | |||
] | |||
} | } | ||
} | |||
}; | }; | ||
var METHODS = { | var METHODS = { | ||
// Shows a window.prompt() message box | |||
msgbox: function (msg) { | |||
window.prompt('Copy to clipboard', msg); | |||
}, | |||
// Show a jQuery dialog | |||
jQueryDiag: function (msg) { | |||
var target_format = "<form name='target'>Format: <select name='selection' onChange='updateTarget()'><option value='0'>to wiki</option><option value='1'>to forum</option></select></form>"; | |||
var diagDiv = $('<div id="MyDialog"><textarea id="quotedtext" rows="10"cols="80" style="width: 290px; height: 290px">' + msg + '</textarea>' + target_format + '</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 MONTHS = [ | |||
var MONTHS = [ | 'Jan', | ||
'Feb', | |||
'Mar', | |||
'Apr', | |||
'May', | |||
'Jun', | |||
'Jul', | |||
'Aug', | |||
'Sep', | |||
'Oct', | |||
'Nov', | |||
'Dec' | |||
]; | |||
// Conversion for forum emoticons | // Conversion for forum emoticons | ||
var EMOTICONS = [ | var EMOTICONS = [ | ||
[/:shock:/g, | |||
'O_O'], | |||
[ | |||
/:lol:/g, | |||
'(lol)' | |||
], | |||
[ | |||
/:oops:/g, | |||
':$' | |||
], | |||
[ | |||
/:cry:/g, | |||
';(' | |||
], | |||
[ | |||
/:evil:/g, | |||
'>:)' | |||
], | |||
[ | |||
/:twisted:/g, | |||
'3:)' | |||
], | |||
[ | |||
/:roll:/g, | |||
'(eye roll)' | |||
], | |||
[ | |||
/:wink:/g, | |||
';)' | |||
], | |||
[ | |||
/:!:/g, | |||
'(!)' | |||
], | |||
[ | |||
/:\?:/g, | |||
'(?)' | |||
], | |||
[ | |||
/:idea:/g, | |||
'(idea)' | |||
], | |||
[ | |||
/:arrow:/g, | |||
'(->)' | |||
], | |||
[ | |||
/:mrgreen:/g, | |||
'xD' | |||
] | |||
]; | ]; | ||
// ################## | // ################## | ||
// # Main functions # | // # Main functions # | ||
// ################## | // ################## | ||
window.addEventListener('load', init); | window.addEventListener('load', init); | ||
// Initialize | // Initialize | ||
function init() { | function init() { | ||
document.onmouseup = instantCquote; | |||
dbLog('init(): Instant-Cquotes script initialized and ready to use'); | |||
} | } | ||
// The main function | |||
function instantCquote() { | function instantCquote() { | ||
var profile = getProfile(), | |||
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; | |||
} | |||
if (!checkValid(selection, profile)) { | |||
dbLog('instantCquote(): Selection is not valid, aborting function'); | |||
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.content = stripWhitespace(output.content); | |||
output = createCquote(output); | |||
outputText(output); | |||
} | } | ||
// Gets to correct profile | |||
function getProfile() { | |||
function getProfile(){ | for (var profile in CONFIG) { | ||
if (window.location.href.match(CONFIG[profile].url_reg) !== null) { | |||
return CONFIG[profile]; | |||
} | } | ||
} | |||
} | } | ||
// Get the HTML code that is selected | |||
function getSelectedHtml() { | 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; | |||
} | } | ||
// Gets the selected text | |||
function getSelectedText() { | function getSelectedText() { | ||
return document.getSelection().toString(); | |||
} | } | ||
// Get the ID of the post | |||
function getPostId(selection, profile, focus) { | function getPostId(selection, profile, focus) { | ||
if (focus !== undefined) { | |||
selection = selection.focusNode.parentNode; | |||
} else { | |||
selection = selection.anchorNode.parentNode; | |||
} | |||
while (selection.id.match(profile.content.idStyle) === null) { | |||
selection = selection.parentNode; | |||
} | |||
return selection.id; | |||
} | } | ||
// Checks that the selection is valid | |||
function checkValid(selection, profile) { | function checkValid(selection, profile) { | ||
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 { | } else { | ||
selection_cp = selection_cp.parentNode; | |||
} | } | ||
} | |||
} | } | ||
} | |||
ret = ret && (getPostId(selection, profile) === getPostId(selection, profile, 1)); | |||
return ret; | |||
} | } | ||
// Extracts the raw text from a certain place, using an XPath | |||
function extractFieldInfo(profile, id, field) { | function extractFieldInfo(profile, id, field) { | ||
if (field === 'content') { | |||
return profile[field].selection(); | |||
} else { | |||
var xpath = '//*[@id="' + id + '"]/' + profile[field].xpath; | |||
return document.evaluate(xpath, document, null, XPathResult.STRING_TYPE, null).stringValue; | |||
} | |||
} | } | ||
// Change the text using spcified transformations | |||
function applyTransformations(fieldInfo, trans) { | function applyTransformations(fieldInfo, trans) { | ||
if (typeof trans === 'function') { | |||
return trans(fieldInfo); | |||
} else if (Array.isArray(trans)) { | |||
for (var i = 0; i < trans.length; i++) { | |||
fieldInfo = trans[i](fieldInfo); | |||
dbLog('applyTransformations(): Multiple transformation, transformation after loop #' + (i + 1) + ':\n\'' + fieldInfo + '\''); | |||
} | } | ||
return fieldInfo; | |||
} | |||
} | } | ||
// Formats the quote | |||
function createCquote(data) { | function createCquote(data) { | ||
var wikiText = '{{FGCquote\n' + ((data.content.match(/^\s*?{{cquote/) === null) ? '|1= ' : '| ') + data.content + '\n' + | |||
'|2= {{cite web\n' + | |||
' | url = ' + data.url + '\n' + | |||
' | title = ' + nowiki(data.title) + '\n' + | |||
' | author = ' + nowiki(data.author) + '\n' + | |||
' | date = ' + datef(data.date) + '\n' + | |||
' }}\n' + | |||
'}}'; | |||
return wikiText; | |||
} | } | ||
// Output the text. | // Output the text. | ||
// Tries the jQuery dialog, and falls back to window.prompt() | // Tries the jQuery dialog, and falls back to window.prompt() | ||
function outputText(msg){ | |||
function outputText(msg) { | |||
try { | |||
METHODS.jQueryDiag(msg); | |||
} | |||
catch (err) { | |||
msg = msg.replace(/<\/syntaxhighligh(.)>/g, '</syntaxhighligh$1'); | |||
METHODS.msgbox(msg); | |||
} | |||
} | } | ||
// ############# | // ############# | ||
// # Utilities # | // # Utilities # | ||
| Line 411: | Line 456: | ||
function extract(regex) { | function extract(regex) { | ||
return function (text) { | |||
return text.match(regex) [1]; | |||
}; | |||
} | } | ||
function prepend(prefix) { | function prepend(prefix) { | ||
return function (text) { | |||
return prefix + text; | |||
}; | |||
} | } | ||
function removeComments(html) { | function removeComments(html) { | ||
return html.replace(/<!--.*?-->/g, ''); | |||
} | } | ||
// Not currently used (as of June 2015), but kept just in case | |||
function escapePipes(html) { | function escapePipes(html) { | ||
html = html.replace(/\|\|/g, '{{!!}}'); | |||
html = html.replace(/\|\-/g, '{{!-}}'); | |||
return html.replace(/\|/g, '{{!}}'); | |||
} | } | ||
// Converts HTML <a href="...">...</a> tags to wiki links, internal if possible. | // 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; | |||
} | } | ||
// Converts images, including images in <a> links | |||
function img2link(html) { | function img2link(html) { | ||
html = html.replace(/<a[^<]*?href="([^<]*?)"[^<]*?><img.*?src="http:\/\/wiki\.flightgear\.org\/images\/.*?\/.*?\/(.*?)".*?><\/a>/g, '[[File:$2|250px|link=$1]]'); | |||
html = html.replace(/<img.*?src="http:\/\/wiki\.flightgear\.org\/images\/.*?\/.*?\/(.*?)".*?>/g, '[[File:$1|250px]]'); | |||
html = html.replace(/<a[^<]*?href="([^<]*?)"[^<]*?><img.*?src="(.*?)".*?><\/a>/g, '(see [$2 image], links to [$1 here])'); | |||
return html.replace(/<img.*?src="(.*?)".*?>/g, '(see the [$1 linked image])'); | |||
} | } | ||
// Converts smilies | |||
function forum_smilies2text(html) { | function forum_smilies2text(html) { | ||
html = html.replace(/<img src="\.\/images\/smilies\/icon_.*?\.gif" alt="(.*?)".*?>/g, '$1'); | |||
for (var i = 0; i < EMOTICONS.length; i++) { | |||
html = html.replace(EMOTICONS[i][0], EMOTICONS[i][1]); | |||
} | |||
return html; | |||
} | } | ||
// Converts font formatting | |||
function forum_fontstyle2wikistyle(html) { | function forum_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'); | |||
} | } | ||
// Converts code blocks | |||
function forum_code2syntaxhighlight(html) { | function forum_code2syntaxhighlight(html) { | ||
var list = html.match(/<dl class="codebox">.*?<code>(.*?)<\/code>.*?<\/dl>/g), | |||
data = [ | |||
]; | |||
if (list === null) return html; | |||
for (var n = 0; n < list.length; n++) { | |||
data = html.match(/<dl class="codebox">.*?<code>(.*?)<\/code>.*?<\/dl>/); | |||
html = html.replace(data[0], processCode(data)); | |||
} | |||
return html; | |||
} | } | ||
// Strips any whitespace from the beginning and end of a string | |||
function stripWhitespace(html) { | |||
function stripWhitespace(html){ | html = html.replace(/^\s*?(\S)/, '$1') | ||
return html.replace(/(\S)\s*?\z/, '$1'); | |||
} | } | ||
// Process code, including basic detection of language | // Process code, including basic detection of language | ||
function processCode(data) { | |||
var lang = '', | |||
code = data[1]; | |||
code = code.replace(/ /g, ' '); | |||
if (code.match(/=?.*?\(?.*?\)?;/) !== null) lang = 'nasal'; | |||
if (code.match(/<.*?>.*?<\/.*?>/) !== null || code.match(/<!--.*?-->/) !== null) lang = 'xml'; | |||
code = code.replace(/<br\/?>/g, '\n'); | |||
return '<syntaxhighlight lang="' + lang + '" enclose="div">\n' + code + '\n</syntaxhighlight>'; | |||
} | } | ||
// Converts quote blocks to Cquotes | |||
function forum_quote2cquote(html) { | function forum_quote2cquote(html) { | ||
html = html.replace(/<blockquote class="uncited"><div>(.*?)<\/div><\/blockquote>/g, '{{cquote|$1}}'); | |||
if (html.match(/<blockquote>/g) === null) return html; | |||
var numQuotes = html.match(/<blockquote>/g).length; | |||
for (var n = 0; n < numQuotes; n++) { | |||
html = html.replace(/<blockquote><div><cite>(.*?) wrote.*?:<\/cite>(.*?)<\/div><\/blockquote>/, '{{cquote|$2|$1}}'); | |||
} | |||
return html; | |||
} | } | ||
// Converts videos to wiki style | // Converts videos to wiki style | ||
function vid2wiki(html) { | |||
// YouTube | |||
html = html.replace(/<div class="video-wrapper">\s.*?<div class="video-container">\s*?<iframe class="youtube-player".*?width="(.*?)" height="(.*?)" src="http:\/\/www\.youtube\.com\/embed\/(.*?)".*?><\/iframe>\s*?<\/div>\s*?<\/div>/g, '{{#ev:youtube|$3|$1x$2}}'); | |||
// Vimeo | |||
html = html.replace(/<iframe src="http:\/\/player\.vimeo\.com\/video\/(.*?)\?.*?" width="(.*?)" height="(.*?)".*?>.*?<\/iframe>/g, '{{#ev:vimeo|$1|$2x$3}}'); | |||
return html.replace(/\[.*? Watch on Vimeo\]/g, ''); | |||
} | } | ||
// Not currently used (as of June 2015), but kept just in case | |||
function escapeEquals(html) { | function escapeEquals(html) { | ||
return html.replace(/=/g, '{{=}}'); | |||
} | } | ||
// <br> to newline. | |||
function forum_br2newline(html) { | function forum_br2newline(html) { | ||
html = html.replace(/<br\/?><br\/?>/g, '\n'); | |||
return html.replace(/<br\/?>/g, '\n\n'); | |||
} | } | ||
// Forum list to wiki style | |||
function list2wiki(html) { | function list2wiki(html) { | ||
var list = html.match(/<ul>(.*?)<\/ul>/g); | |||
if (list !== null) { | |||
for (var i = 0; i < list.length; i++) { | |||
html = html.replace(/<li>(.*?)<\/li>/g, '* $1\n'); | |||
} | } | ||
} | |||
list = html.match(/<ol.*?>(.*?)<\/ol>/g); | |||
if (list !== null) { | |||
for (var i = 0; i < list.length; i++) { | |||
html = html.replace(/<li>(.*?)<\/li>/g, '# $1\n'); | |||
} | } | ||
} | |||
html = html.replace(/<\/?[uo]l>/g, ''); | |||
return html; | |||
} | } | ||
function nowiki(text) { | function nowiki(text) { | ||
return '<nowiki>' + text + '</nowiki>'; | |||
} | } | ||
// 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 dbLog(message) { | function dbLog(message) { | ||
if (Boolean(DEBUG)) console.log(message); | |||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
[[Category:Wiki maintenance]] | [[Category:Wiki maintenance]] | ||