Skip to content

Instantly share code, notes, and snippets.

@A-312
Last active July 25, 2018 10:41
Show Gist options
  • Save A-312/fa7ac99f1336b8ff06cc9013f2e59681 to your computer and use it in GitHub Desktop.
Save A-312/fa7ac99f1336b8ff06cc9013f2e59681 to your computer and use it in GitHub Desktop.
Userscript pour citer la selection sur Zeste de savoir - Version 1.41
// ==UserScript==
// @name ZDS - Citer la selection
// @description Citer la selection.
// @namespace zds_citer_selection_a-312
// @author A-312
// @version 1.41
// @include https://zestedesavoir.com/forums/sujet/*
// @include https://zestedesavoir.com/tutoriels/*
// @include https://zestedesavoir.com/billets/*
// @require http://code.jquery.com/jquery-3.3.1.min.js
// ==/UserScript==zds
(function($, undefined) {
//$(".message-actions").off("click", "[data-ajax-input='cite-message']").on("click", "[data-ajax-input='cite-message']", function(e) {
$(".message-actions").off("click", "[data-ajax-input='cite-message']");
$(".message-actions .cite[data-ajax-input='cite-message']")
.parent().on("click", "[data-ajax-input='cite-message']", function(e) {
var $act = $(this),
$editor = $(".md-editor");
// quote the selection
var doAjax = !(function() {
var $message = $act.closest(".message"),
$msgcontent = $message.find("[itemprop=text]"),
username = $message.find("[itemprop=name]").text(),
href = $message.find("> .message-metadata > a.date")[0].href;
if (window.getSelection && window.getSelection() && window.getSelection().rangeCount > 0) {
var selection = window.getSelection(),
range = selection.getRangeAt(0);
console.log("dddddddd", selection, range);
if (selection.isCollapsed)
return false;
if (!$(range.startContainer).closest($msgcontent)[0])
return false;
if (!$(range.endContainer).closest($msgcontent)[0]) {
var node = $msgcontent[0].childNodes;
node = node[node.length - 1];
range.setEnd(node, node.nodeValue.length);
}
var content = html2MD_rangeToVirtualDOM(range);
var markdown = window.html2markdown(content);
markdown = markdown.replace(/(^|\n)/g, "$1> ");
markdown += "\nSource:[" + username + "](" + href + ")";
$editor.val($editor.val() + markdown + "\n\n");
return true;
}
return false;
})();
if (doAjax) {
$.ajax({
url: $act.attr("href"),
dataType: "json",
success: function(data) {
$editor.val($editor.val() + data.text + "\n\n");
}
});
}
// scroll to the textarea and focus the textarea
$("html, body").animate({
scrollTop: $editor.offset().top
}, 500);
$editor.focus();
e.stopPropagation();
e.preventDefault();
});
})(jQuery);
/* ===== Zeste de Savoir ====================================================
Parse HTML to Markdown
---------------------------------
Author: A-312
========================================================================== */
(function(window, $, undefined) {
"use strict";
window.html2markdown = function(aParam) {
var abbr = {},
footer = "";
var $message = (aParam instanceof $) ? aParam : $("<div></div>").append(aParam),
element, text,
boolstisP = !!($message.children(":first").is("p"));
$message.find("p").prepend("\n");
while ($message[0].children.length > 0 && $message[0].children[0]) {
element = $message[0].children[0];
if (!html2Text(element)) {
if ($(element).children()[0]) {
recursiveWalk(element);
}
element.parentNode.replaceChild(document.createTextNode($(element).text()), element);
}
}
$.each(abbr, function(word, title) {
footer += "\n\n*[" + word + "]: " + title;
});
text = $message.text();
if (boolstisP) //remove first new line
text = text.replace(/^\n/, "");
text = text.replace(/\n( +)\n/g, "\n\n");
return text + footer;
};
var makePathOfHTMLElements = function(parents) {
return parents.map(function() {
var innerTag = "",
$el = $(this);
["id", "class"].map(function(key) {
if ($el.attr(key))
innerTag += " " + key + '="' + $el.attr(key) + '"';
});
return "<" + $el[0].tagName + innerTag + "></" + $el[0].tagName + ">";
}).get();
};
/*
* Cette fonction ajoute le bon niveau HTML à la selection. Pour éviter un bug
* lorsque l'utilisateur selectionne uniquement l'intérieur d'une balise code,
* ou s'il selectionne uniquement l'auteur de la citation.
*
* Fonctionnement : La fonction va ajouter les parents manquants aux éléments,
* pour obtenir le même niveau HTML à chaque éxecution.
*/
window.html2MD_rangeToVirtualDOM = function(range) {
var content = range.cloneContents(),
slice = (content.childNodes[0].nodeType === Node.ELEMENT_NODE) ? 1 : 0,
parents = $(range.startContainer).parents().slice(slice);
// .slice(1) car startContainer renvoit le node et non l'element
parents = makePathOfHTMLElements(parents);
var index = parents.indexOf('<DIV class="message-content"></DIV>');
if (index === -1) { // On anticipe un bug : provoqué en cas de changement du template/html.
console.error("ligne 68: citerlaselection !");
return content;
}
parents = parents.slice(0, index - 1); // -1 pour DIV car .message-content > DIV > [contenuMSG]
if (parents && parents.length > 0) {
var $virtualDOM = $(parents[0]);
for (var i = 1, $child = $virtualDOM; i < parents.length; i++)
$child = $(parents[i]).append($child);
$virtualDOM.append(content).append("\n");
return (parents.length > 1) ? $virtualDOM.parents().last() : $virtualDOM;
}
return content;
};
var recursiveWalk = function(element) {
if ($(element).children()[0]) {
$(element).children().each(function() {
if (html2Text(this))
return;
else
recursiveWalk(this);
});
}
return element;
};
var html2Text = function(element) {
var text = $(element).text();
var repaceWithTextNode = function(string, oldChild) {
var child = (oldChild || element);
child.parentNode.replaceChild(document.createTextNode(string), child);
};
var html2MD = function(obj) {
return $(recursiveWalk(obj)).text();
};
//console.log("-->", element.nodeName, $(element).html());
if (element.nodeName === "BR") {
repaceWithTextNode(" ");
}
//bold
else if (element.nodeName === "STRONG") {
repaceWithTextNode("**" + html2MD(element) + "**");
}
//italic
else if (element.nodeName === "EM") {
repaceWithTextNode("*" + html2MD(element) + "*");
}
//strike
else if (element.nodeName === "DEL") {
repaceWithTextNode("~~" + html2MD(element) + "~~");
}
//sup
else if (element.nodeName === "SUP") {
repaceWithTextNode("^" + html2MD(element) + "^");
}
//sub
else if (element.nodeName === "SUB") {
repaceWithTextNode("~" + html2MD(element) + "~");
}
//abbr & footnote
else if (element.nodeName === "ABBR") {
repaceWithTextNode(text);
abbr[text] = element.title;
}
//key
else if (element.nodeName === "KBD") {
repaceWithTextNode("||" + text + "||");
}
//titles h1, h2, h3, h4
else if (element.nodeName[0] === "H" && /^H[3-6]$/.test(element.nodeName)) {
repaceWithTextNode("\n" + new Array(element.nodeName[1] - 1).join("#") + " " + html2MD(element));
}
//ul & ol
else if (element.nodeName === "LI") {
if (!$(this).data("num") && $(element).parent()[0].nodeName === "OL") {
$(element).parent().children().each(function() {
$(this).data("num", ($(this).index() + 1) + ". ");
});
}
repaceWithTextNode(($(element).data("num") || "- ") + html2MD(element));
}
//center & right
else if (element.nodeName === "DIV" && $(element).attr("align")) {
text = html2MD(element);
text = text.replace(/^\s*(.*?)\s*$/, "$1") || text;
if ($(element).attr("align") === "center") {
repaceWithTextNode("\n-> " + text + " <-");
} else if ($(element).attr("align") === "right") {
repaceWithTextNode("\n-> " + text + " ->");
} else {
return false;
}
}
//quote
else if (element.nodeName === "FIGURE" && $(element).children("blockquote")[0]) {
var $blockquote = $(element).children("blockquote");
recursiveWalk($blockquote);
text = $blockquote.children("p").text().replace(/( {2})?\n/g, " \n> ");
text += "\nSource:" + html2MD($(element).children("figcaption")).replace(/^\n/, "");
repaceWithTextNode(text);
}
//image
else if (element.nodeName === "FIGURE" && $(element).children("img")[0]) {
var src = $(element).children("img").attr("src"),
title = $(element).children("figcaption").text();
repaceWithTextNode("![" + title + "](" + src + ")");
}
//inline image & smiley
else if (element.nodeName === "IMG") {
if ($(element).attr("src").indexOf("/static/smileys") === 0)
repaceWithTextNode(element.alt);
else
repaceWithTextNode("![" + element.alt + "](" + $(element).attr("src") + ")");
}
//link
else if (element.nodeName === "A") {
text = html2MD(element);
if ($(element).hasClass("spoiler-title")) {
repaceWithTextNode(""); // remove "Afficher/Masquer le contenu masqué"
} else if (text.indexOf("http") === 0) {
repaceWithTextNode($(element).attr("href"));
} else {
repaceWithTextNode("[" + text + "](" + $(element).attr("href") + ")");
}
}
//table
/*
*/
/* ... */
else if (element.nodeName === "DIV" && $(element).is(".information, .question, .warning, .error, .spoiler")) {
var makeBlock = function(type) {
var text = html2MD($(element));
text = text
.replace(/( {2})?\n/g, " \n| ")
.replace(/^ \n\|\s+\n/, "")
.replace(/(\n\|\s+)+$/, "");
repaceWithTextNode("\n[[" + type + "]]\n" + text);
};
//information
if ($(element).hasClass("information")) {
makeBlock("information");
}
//question
else if ($(element).hasClass("question")) {
makeBlock("question");
}
//attention
else if ($(element).hasClass("warning")) {
makeBlock("attention");
}
//error
else if ($(element).hasClass("error")) {
makeBlock("erreur");
}
//secret
else if ($(element).hasClass("spoiler")) {
makeBlock("secret"); // see A (remove "Afficher/Masquer le contenu masqué")
} else {
return false;
}
}
//monospace
else if (element.nodeName === "CODE") {
repaceWithTextNode("`" + text + "`");
}
//blockcode
else if (element.nodeName === "TABLE" && $(element).hasClass("codehilitetable")) {
text = $(element).find("td.code pre").text();
if (text.split("\n").length <= 2)
repaceWithTextNode("\n " + text.replace(/\n$/, ""));
else
repaceWithTextNode(" \n```\n" + text + "```");
}
//math
else if (element.nodeName === "MATHJAX") {
text = $(element).children("script[type='math/tex']").text();
repaceWithTextNode("$" + text + "$");
}
//iframe
else if (element.nodeName === "FIGURE" && $(element).children("iframe")[0]) {
text = html2MD($("<div></div>").append($(element).children("iframe")));
text += "\nVideo:" + html2MD($(element).children("figcaption")).replace(/^\n/, "");
repaceWithTextNode(text);
}
//inline iframe
else if (element.nodeName === "IFRAME") {
var embeds = [
/^https?:\/\/www\.dailymotion\.com\/embed\/video\/(.+)$/,
/^https?:\/\/www\.metacafe\.com\/embed\/(.+)\/$/,
/^https?:\/\/www\.veoh\.com\/videodetails2\.swf\?.*permalinkId\=(.+)$/,
/^https?:\/\/player\.vimeo\.com\/video\/(.+)$/,
/^https?:\/\/screen\.yahoo\.com\/(.+)\/?/, // don't add $
/^https?:\/\/www\.youtube\.com\/embed\/(.+)$/,
/^https?:\/\/jsfiddle\.net\/(.*)\/(.*)\// // don't add $
];
var urls = [
"http://www.dailymotion.com/video/$1",
"http://www.metacafe.com/watch/$1/",
"http://www.veoh.com/watch/$1",
"http://vimeo.com/$1",
"http://screen.yahoo.com/$1",
"http://youtu.be/$1",
"http://jsfiddle.net/$1/$2/"
];
text = $(element).attr("src");
for (var i = 0; i < embeds.length; i++) {
if (embeds[i].test(text)) {
repaceWithTextNode("\n!(" + text.replace(embeds[i], urls[i]) + ")");
break;
}
}
}
//hr
else if (element.nodeName === "HR") {
repaceWithTextNode("\n\n------\n\n");
}
//other
else {
return false;
}
return true;
};
})(window, jQuery);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment