Greasy Fork

Greasy Fork is available in English.

Instant-Cquotes

Automatically converts FlightGear mailing list and forum quotes into MediaWiki markup (cquotes).

当前为 2016-05-03 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name        Instant-Cquotes
// @version     0.30
// @description Automatically converts FlightGear mailing list and forum quotes into MediaWiki markup (cquotes).
// @description:it Converte automaticamente citazioni dalla mailing list e dal forum di FlightGear in marcatori MediaWiki (cquote).
// @author      Hooray, bigstones, Philosopher, Red Leader & Elgaton (2013-2016)
// @icon        http://wiki.flightgear.org/images/6/62/FlightGear_logo.png
// @match       https://sourceforge.net/p/flightgear/mailman/*
// @match       http://sourceforge.net/p/flightgear/mailman/*
// @match       https://forum.flightgear.org/*
// @namespace   http://wiki.flightgear.org/FlightGear_wiki:Instant-Cquotes
// @run-at      document-end
// @require     https://code.jquery.com/jquery-2.1.4.min.js
// @require     https://code.jquery.com/ui/1.11.4/jquery-ui.min.js
// @resource    jQUI_CSS https://code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css
// @grant       GM_setValue
// @grant       GM_getValue
// @grant       GM_addStyle
// @grant       GM_getResourceText
// @grant       GM_setClipboard
// @grant       GM_xmlhttpRequest
// @noframes
// ==/UserScript==
//
// This work has been released into the public domain by their authors. This
// applies worldwide.
// In some countries this may not be legally possible; if so:
// The authors grant anyone the right to use this work for any purpose, without
// any conditions, unless such conditions are required by law.
//
// Check if Greasemonkey/Tampermonkey is available
try {
  // TODO: add version check for clipboard API and check for TamperMonkey/Scriptish equivalents ?
  GM_addStyle(GM_getResourceText('jQUI_CSS'));
  // http://wiki.greasespot.net/GM_info
  var scriptVersion = ' (v'+GM_info.script.version+')';
  //console.log(GM_info);
}
catch (error) {
}
'use strict';
// Define constants
var DEBUG = false;

// set this to true continue working on the new mode supporting
// asynchronous content fetching via AJAX
var USE_NG=false;

function set_persistent(key,value) {
  // https://wiki.greasespot.net/GM_setValue
  GM_setValue(key,value);
} //make_persistent

function get_persistent(key, default_value) {
  // https://wiki.greasespot.net/GM_getValue
  return GM_getValue(key,default_value);
} // get_persistent

// hash with supported websites/URLs

var CONFIG = {
  'Mailing list': {
    url_reg: "^(http|https):\/\/sourceforge\.net\/p\/flightgear\/mailman\/.*/",
    content: {
      selection: getSelectedText,
      idStyle: /msg[0-9]{8}/,
      parentTag: [
        'tagName',
        'PRE'
      ]
    },
// regex/xpath and transformations for extracting various required fields
    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('https://sourceforge.net')
    }
  },
// next website/URL (forum)
  'FlightGear forum': {
    url_reg: /https:\/\/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('https://forum.flightgear.org')
      ]
    }
  }
};

// this being a greasemonkey user-script, we are not 
// subject to usual browser restrictions

function setClipboard(msg) {
  // http://wiki.greasespot.net/GM_setClipboard
  GM_setClipboard(msg);
}

// hash to map URLs (wiki article, issue tracker, sourceforge link, forum thread etc) to existing wiki templates

var URL2TemplateTable = {
// placeholder for now
}; // TemplateTable

var EventHandlers = {
 updateTarget: function() {alert("not yet implement");},
 updateFormat: function() {alert("not yet implement");},
}; // EventHandlers

// output methods (alert and jQuery for now)
var METHODS = {
  // Shows a window.prompt() message box
  msgbox: function (msg) {
    window.prompt('Copy to clipboard'+scriptVersion, msg);
    setClipboard(msg);
  },
  // Show a jQuery dialog
  jQueryDiag: function (msg) {

    // WIP: add separate Target/Format combo boxes for changing the template to be used (e.g. for refs instead of quotes)
    var target_format = "<form name='target'>Target: <select name='selection' onchange='EventHandlers.updateTarget();'><option value='0'>to wiki</option><option value='1'>to forum</option></select>Format: <select name='format' onchange='EventHandlers.updateFormat();'><option value='0'>refonly</option><option value='1'>fgcquote</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'+scriptVersion,
      modal: true,
      width: 'auto',
      buttons: [
        {
          text: 'Setup',
          click: setupDialog
        },
        {
          text: 'Select all',
          click: function () {
            setClipboard(msg);
            $('#quotedtext').select();
          }
        },
        {
          text: 'OK',
          click: function () {
            setClipboard(msg);
            $(this).dialog('close');
          }
        }
      ]
    };
    diagDiv.dialog(diagParam);
  }
};
var MONTHS = [
  'Jan',
  'Feb',
  'Mar',
  'Apr',
  'May',
  'Jun',
  'Jul',
  'Aug',
  'Sep',
  'Oct',
  'Nov',
  'Dec'
];
// Conversion for forum 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 #
// ##################
window.addEventListener('load', init);
dbLog('Instant Cquotes: page load handler registered');
// Initialize
function init() {
  dbLog('page load handler');
   if (Boolean(USE_NG)) {
    dbLog("devel version (WIP)");
    document.onmouseup = instantCquoteNG;
   }else {
     dbLog("standard version");
     document.onmouseup = instantCquote;
   }
}

function OpenLink(url, callback){
  // http://wiki.greasespot.net/GM_xmlhttpRequest
  GM_xmlhttpRequest({
  method: "GET",
  url: url,
  onload: callback
});
}

// an experimental version (non-functional for now)
function instantCquoteNG() {
  dbLog("experimental NN callback triggered");
  
  //self-test
  OpenLink("http://sourceforge.net/p/flightgear/mailman/message/27369425/",function(response) {
    console.log("ajax request status:"+response.statusText);
  });
  
  
} // instantCquoteNG


// The main function

function instantCquote() {
  var profile = getProfile(),
  selection = document.getSelection(),
  output = {
  },
  field = {
  };
  try {
    var post_id = getPostId(selection, profile);
  } 
  catch (error) {
    dbLog("Failed extracting post id\nProfile:"+profile)
    return;
  }
  if (selection.toString() === '') {
    dbLog('No text is selected, aborting function');
    return;
  }
  if (!checkValid(selection, profile)) {
    dbLog('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('Field \'' + field + '\' before transformation:\n\'' + fieldData + '\'');
      fieldData = applyTransformations(fieldData, transform);
      dbLog('Field \'' + field + '\' after transformation:\n\'' + fieldData + '\'');
    }
    output[field] = fieldData;
  }
  output.content = stripWhitespace(output.content);
  output = createCquote(output);
  outputText(output);
}
// Gets to correct profile

function setupDialog() {
  alert("configuration dialog is not yet implemented");
} // setupDialog

function getProfile() {
  for (var profile in CONFIG) {
    if (window.location.href.match(CONFIG[profile].url_reg) !== null) {
      dbLog("Matching profile found");
      
      var invocations=get_persistent(GM_info.script.version,0);
      dbLog("Number of script invocations for version "+GM_info.script.version+" is:"+invocations);
      if (invocations==0) {
        var response = confirm("This is your first time running version "+GM_info.script.version+ "\nConfigure now?");
       if (response){
       // show configuration dialog (jQuery)
         setupDialog();
     }
     else{
        
     } // don't configure
      }
      // increment number of invocations, use the version number as the key, to prevent the config dialog from showing up again
      set_persistent(GM_info.script.version, invocations+1)
      return CONFIG[profile];
    }
    dbLog("Could not find matching URL in getProfile() call!")
  }
}
// Get the HTML code that is selected

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() {
  return document.getSelection().toString();
}
// Get the ID of the post

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) {
  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;
        }
      }
    }
  }
  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) {
  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 specified transformations

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, light_quotes=true) {
// skip FGCquote (experimental)
if (light_quotes) return nonQuotedRef(data);

  var date_added = new Date();
  var wikiText = '{{FGCquote\n' + ((data.content.match(/^\s*?{{cquote/) === null) ? '|1= ' : '| ') + data.content + '\n' +
  '|2= ' + createCiteWeb(data)+'\n'+
  '}}';
  return wikiText;
}

function nonQuotedRef(data) {
 return addContentBlob(data) + createRefCite(data);
}

// 
function addContentBlob(data) {
return data.content;
}

// wrap citation in ref tags
function createRefCite(data) {
 return '<ref>'+createCiteWeb(data)+'</ref>';
}

function createCiteWeb(data) {
  var date_added = new Date();
  var wikiText = '{{cite web\n' +
  '  |url    = ' + data.url + '\n' +
  '  |title  = ' + nowiki(data.title) + '\n' +
  '  |author = ' + nowiki(data.author) + '\n' +
  '  |date   = ' + datef(data.date) + '\n' +
  '  |added  = ' + datef(date_added.toDateString()) + '\n' +
  '  |script_version = ' + GM_info.script.version + '\n' +
  '  }}\n';
  return wikiText;
}


// Output the text.
// Tries the jQuery dialog, and falls back to window.prompt()

function outputText(msg) {
  try {
    METHODS.jQueryDiag(msg);
    // TODO: unify code & call setClipboard() here
  } 
  catch (err) {
    msg = msg.replace(/&lt;\/syntaxhighligh(.)>/g, '</syntaxhighligh$1');
    METHODS.msgbox(msg);
  }
}
// #############
// # 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, '');
}
// Not currently used (as of June 2015), but kept just in case

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.

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
  // TODO: identify forum/devel list links, and use the AJAX/OpenLink helper to get a title/subject for unnamed links (using the existing xpath/regex helpers for that)
  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) {
  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) {
  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) {
  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) {
  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) {
  html = html.replace(/^\s*?(\S)/, '$1')
  return html.replace(/(\S)\s*?\z/, '$1');
}
// Process code, including basic detection of language

function processCode(data) {
  var lang = '',
  code = data[1];
  code = code.replace(/&nbsp;/g, ' ');
  if (code.match(/=?.*?\(?.*?\)?;/) !== null) lang = 'nasal';
  if (code.match(/&lt;.*?&gt;.*?&lt;\/.*?&gt;/) !== null || code.match(/&lt;!--.*?--&gt;/) !== null) lang = 'xml';
  code = code.replace(/<br\/?>/g, '\n');
  return '<syntaxhighlight lang="' + lang + '" enclose="div">\n' + code + '\n&lt;/syntaxhighlight>';
}
// Converts quote blocks to Cquotes

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

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) {
  return html.replace(/=/g, '{{=}}');
}
// <br> to newline.

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) {
  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) {
  return '<nowiki>' + text + '</nowiki>';
}
// Returns the correct ordinal adjective

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
function datef(text) {
  var date = new Date(text);
  return MONTHS[date.getMonth()] + ' ' + date.getDate() + ordAdj(date.getDate()) + ', ' + date.getFullYear();
}
function underscore2Space(str) {
  return str.replace(/_/g, ' ');
}
function dbLog(message) {
  if (Boolean(DEBUG)) {
    console.log("Instant cquotes:"+message);
  }
}