ivnote-web-clipper

Choose your favorite DOM node and its content will be automatically copied to the clipboard in markdown format

Você precisará instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

You will need to install an extension such as Tampermonkey to install this script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Você precisará instalar uma extensão como o Tampermonkey para instalar este script.

Você precisará instalar um gerenciador de scripts de usuário para instalar este script.

(Eu já tenho um gerenciador de scripts de usuário, me deixe instalá-lo!)

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

(Eu já possuo um gerenciador de estilos de usuário, me deixar fazer a instalação!)

// ==UserScript==
// @name         ivnote-web-clipper
// @version      0.8.1
// @description  Choose your favorite DOM node and its content will be automatically copied to the clipboard in markdown format
// @author       Ivan Jiang
// @match        http://*/*
// @match        https://*/*
// @grant        GM_addStyle
// @require      https://cdn.bootcss.com/to-markdown/3.0.3/to-markdown.min.js
// @namespace http://ivnote.xyz
// ==/UserScript==

/* globals GM_addStyle, toMarkdown */
GM_addStyle(` .ivnote-web-clipper-hover { background: rgba(124, 201, 191, .7); }
.ivnote-web-clipper-icon {
  position: fixed; bottom: 15px; right: 15px;
  height: 50px; width: 50px; border-radius: 50%;
  background: RGBA(124, 201, 191, .6) url('https://github.com/iplus26/ivnote/raw/master/web-clipper/toggle-icon.png') no-repeat center center / 80%;
  opacity: .7; cursor: pointer;
  transition: all .3s ease-in-out;
  z-index: 9999;
}
.ivnote-web-clipper-icon.ivnote-web-clipper-enable {
  opacity: 1; transform: rotate(180deg);
  right: 50px;
}
.ivnote-web-clipper-icon:hover {
  opacity: 1;
  transform: rotate(180deg)
}`);

(function() {
'use strict';

// Utils =========================================================================
var classCache = {};

var _ = {
    hasClass: hasClass,
    addClass: addClass,
    removeClass: removeClass,
};

function hasClass(node, name) {
    if (!name) {
        return false;
    }
    return classRE(name).test(className(node));
}

function addClass(node, name) {
    if (!name) {
        return node;
    }
    if (!('className' in node)) {
        return;
    }
    var classList = [];
    var cls = className(node); // get node's className
    name.split(/\s+/g).forEach(function(klass) {
        if (!hasClass(node, klass)) {
            classList.push(klass);
        }
    });
    return classList.length && className(node, cls + (cls ? ' ' : '') + classList.join(' '));
}

function removeClass(node, name) {
    if (!('className' in node)) {
        return;
    }
    if (name === undefined) {
        return className(node, '');
    }
    let cls = className(node);
    name.split(/\s+/g)
        .forEach((klass) => cls = cls.replace(classRE(klass), ' '));
    className(node, cls.trim());
}

function classRE(name) {
    return name in classCache ?
        classCache[name] : (classCache[name] = new RegExp('(^|\\s)' + name + '(\\s|$)'));
}
// access className property while respecting SVGAnimatedString
function className(el, value) {
    var klass = el.className || '',
        svg = klass && klass.baseVal !== undefined;

    // get className
    if (value === undefined) {
        return svg ? klass.baseVal : klass;
    }

    // set className
    return svg ? (klass.baseVal = value) : (el.className = value);
}

// Toggle icon =====================================================================
var toggle = document.createElement('div');
toggle.className = "ivnote-web-clipper-icon";
document.body.appendChild(toggle);

var enable = false;

toggle.addEventListener('click', function(e) {
  if (_.hasClass(toggle, 'ivnote-web-clipper-enable')) {
    // turn it off
    _.removeClass(toggle, 'ivnote-web-clipper-enable');
    enable = false;
  } else {
    // turn it on
    _.addClass(toggle, 'ivnote-web-clipper-enable');
    enable = true;
  }
  e.stopPropagation();
});

// Manipulate the doms ==============================================================
document.addEventListener('mouseover', function(e) {
    if (enable) _.addClass(e.target, 'ivnote-web-clipper-hover');
});

document.addEventListener('mouseout', function(e) {
    if (enable) {
        _.removeClass(e.target, 'ivnote-web-clipper-hover');

        // To be safe
        var select = document.querySelectorAll('.ivnote-web-clipper-hover');
        Array.prototype.slice.call(select).forEach(function(el) { _.removeClass(el, 'ivnote-web-clipper-hover'); });
    }
});

document.addEventListener('click', function(e) {
    if (enable) {
        var el = e.target;

        var fRemoveContent   = function() { return '' }
        var fRemoveInlineTag = function(content) { return content }
        var fRemoveBlockTag  = function(content) { return '\n\n' + content + '\n\n' }

        var generalConverters = [
            // Originally, toMarkdown keeps the tag name of block level elements. 
            {
                filter: ['article', 'div'],
                replacement: fRemoveBlockTag
            },
            {
                filter: ['span'],
                replacement: fRemoveInlineTag
            },
            {
                filter: ['style', 'script'],
                replacement: fRemoveContent
            },
            {
                filter: function(node) {
                    return node.nodeName === 'A' && !node.getAttribute('href');
                },
                replacement: fRemoveInlineTag
            },
            // More cases to create the TR spec.
            {
                filter: 'tr',
                replacement: function(content, node) {
                    var borderCells = '';
                    var alignMap = {
                        left: ':--',
                        right: '--:',
                        center: ':-:'
                    };

                    var headRow = node.parentNode.nodeName === 'THEAD';

                    // Assume ths in first tr in tbody are header cells. (Issue #89)
                    headRow = headRow || node.parentNode.nodeName === 'TBODY' && !node.previousSibling && !node.parentNode.previousSibling;

                    if (headRow) {
                        for (var i = 0; i < node.childNodes.length; i++) {
                            var align = node.childNodes[i].attributes.align;
                            var border = '---';

                            if (align) {
                                border = alignMap[align.value] || border;
                            }

                            borderCells += cell(border, node.childNodes[i]);
                        }
                    }
                    return '\n' + content + (borderCells ? '\n' + borderCells : '');
                }
            }
        ];

        var particularWebsitesConverters = [
            {
                filter: function(node) {
                    return node.nodeName === 'BR' && /^(.+\.)?zhihu.com$/gi.test(location.host)
                },
                replacement: function() {
                    return '\n\n'
                }
            }
        ];

        var markdown = '*The note is clipped from [here](' + location.href + ').*\n\n' +
            (document.title && !el.querySelector('h1') ? '#' + document.title + '\n\n' : '') +
            toMarkdown(el.outerHTML, {
                gfm: true,
                converters: particularWebsitesConverters.concat(generalConverters)
            });
        copyTextToClipboard(markdown);
    }
});

// Internal function of to-markdown
function cell (content, node) {
  var index = Array.prototype.indexOf.call(node.parentNode.childNodes, node);
  var prefix = ' ';
  if (index === 0) prefix = '| ';
  return prefix + content + ' |';
}

// Copy to the clipboard ============================================================
// Copyright (c) Dean Taylor. Original post: http://stackoverflow.com/a/30810322/4158282
function copyTextToClipboard(text) {
  var textArea = document.createElement("textarea");
  textArea.style.position = 'fixed';
  textArea.style.top = 0;
  textArea.style.left = 0;
  textArea.style.width = '2em';
  textArea.style.height = '2em';
  textArea.style.padding = 0;
  textArea.style.border = 'none';
  textArea.style.outline = 'none';
  textArea.style.boxShadow = 'none';
  textArea.style.background = 'transparent';
  textArea.value = text;
  document.body.appendChild(textArea);
  textArea.select();
  try {
    var successful = document.execCommand('copy');
    var msg = successful ? 'successful' : 'unsuccessful';
    console.log('Copying text command was ' + msg);
  } catch (err) {
    console.log('Oops, unable to copy');
  }
  document.body.removeChild(textArea);
}

})();