ivnote-web-clipper

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

当前为 2016-11-02 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name ivnote-web-clipper
  3. // @version 0.4
  4. // @description Choose your favorite DOM node and its content will be automatically copied to the clipboard in markdown format
  5. // @author Ivan Jiang
  6. // @match *://*/*
  7. // @grant GM_addStyle
  8. // @require //cdn.bootcss.com/to-markdown/3.0.3/to-markdown.min.js
  9. // @namespace http://ivnote.xyz
  10. // ==/UserScript==
  11.  
  12. /* globals GM_addStyle */
  13. GM_addStyle(` .ivnote-hover { background: rgba(124, 201, 191, .7); }
  14. .ivnote-web-clipper-icon {
  15. position: fixed; bottom: 15px; right: 15px;
  16. height: 50px; width: 50px; border-radius: 50%;
  17. background: RGBA(124, 201, 191, .6) url('//github.com/iplus26/ivnote/blob/master/toggle-icon.png') no-repeat center center / 80%;
  18. opacity: .7; cursor: pointer;
  19. transition: all .3s ease-in-out;
  20. z-index: 9999;
  21. }
  22. .ivnote-web-clipper-icon.ivnote-web-clipper-enable {
  23. opacity: 1; transform: rotate(180deg);
  24. right: 50px;
  25. }
  26. .ivnote-web-clipper-icon:hover {
  27. opacity: 1;
  28. transform: rotate(180deg)
  29. }`);
  30.  
  31. (function() {
  32. 'use strict';
  33.  
  34. // Utils =========================================================================
  35. var classCache = {};
  36.  
  37. var _ = {
  38. hasClass: hasClass,
  39. addClass: addClass,
  40. removeClass: removeClass,
  41. };
  42.  
  43. function hasClass(node, name) {
  44. if (!name) {
  45. return false;
  46. }
  47. return classRE(name).test(className(node));
  48. }
  49.  
  50. function addClass(node, name) {
  51. if (!name) {
  52. return node;
  53. }
  54. if (!('className' in node)) {
  55. return;
  56. }
  57. var classList = [];
  58. var cls = className(node); // get node's className
  59. name.split(/\s+/g).forEach(function(klass) {
  60. if (!hasClass(node, klass)) {
  61. classList.push(klass);
  62. }
  63. });
  64. return classList.length && className(node, cls + (cls ? ' ' : '') + classList.join(' '));
  65. }
  66.  
  67. function removeClass(node, name) {
  68. if (!('className' in node)) {
  69. return;
  70. }
  71. if (name === undefined) {
  72. return className(node, '');
  73. }
  74. let cls = className(node);
  75. name.split(/\s+/g)
  76. .forEach((klass) => cls = cls.replace(classRE(klass), ' '));
  77. className(node, cls.trim());
  78. }
  79.  
  80. function classRE(name) {
  81. return name in classCache ?
  82. classCache[name] : (classCache[name] = new RegExp('(^|\\s)' + name + '(\\s|$)'));
  83. }
  84. // access className property while respecting SVGAnimatedString
  85. function className(el, value) {
  86. var klass = el.className || '',
  87. svg = klass && klass.baseVal !== undefined;
  88.  
  89. // get className
  90. if (value === undefined) {
  91. return svg ? klass.baseVal : klass;
  92. }
  93.  
  94. // set className
  95. return svg ? (klass.baseVal = value) : (el.className = value);
  96. }
  97.  
  98. // Toggle icon =====================================================================
  99. var toggle = document.createElement('div');
  100. toggle.className = "ivnote-web-clipper-icon";
  101. document.body.appendChild(toggle);
  102.  
  103. var enable = false;
  104.  
  105. toggle.addEventListener('click', function(e) {
  106. if (_.hasClass(toggle, 'ivnote-web-clipper-enable')) {
  107. // turn it off
  108. _.removeClass(toggle, 'ivnote-web-clipper-enable');
  109. enable = false;
  110. } else {
  111. // turn it on
  112. _.addClass(toggle, 'ivnote-web-clipper-enable');
  113. enable = true;
  114. }
  115. e.stopPropagation();
  116. });
  117.  
  118. // Manipulate the doms ==============================================================
  119. document.addEventListener('mouseover', function(e) {
  120. if (enable) _.addClass(e.target, 'ivnote-hover');
  121. });
  122.  
  123. document.addEventListener('mouseout', function(e) {
  124. if (enable) {
  125. _.removeClass(e.target, 'ivnote-hover');
  126.  
  127. // To be safe
  128. var select = document.querySelectorAll('.ivnote-hover');
  129. Array.prototype.slice.call(select).forEach(function(el) { _.removeClass(el, 'ivnote-hover'); });
  130. }
  131. });
  132.  
  133. document.addEventListener('click', function(e) {
  134. if (enable) {
  135. var el = e.target;
  136. var markdown = '*The note is clipped from [here](' + location.href + ').*\n\n' +
  137. (document.title ? '#' + document.title + '\n\n' : '') +
  138. toMarkdown(el.outerHTML, { converters: [{
  139. filter: 'div',
  140. replacement: function(content) { return '\n' + content + '\n'; }
  141. }, {
  142. filter: 'span',
  143. replacement: function(content) { return content; }
  144. }]});
  145. copyTextToClipboard(markdown);
  146. }
  147. });
  148.  
  149. // Copy to the clipboard ============================================================
  150. // Copyright (c) Dean Taylor. Original post: http://stackoverflow.com/a/30810322/4158282
  151. function copyTextToClipboard(text) {
  152. var textArea = document.createElement("textarea");
  153. textArea.style.position = 'fixed';
  154. textArea.style.top = 0;
  155. textArea.style.left = 0;
  156. textArea.style.width = '2em';
  157. textArea.style.height = '2em';
  158. textArea.style.padding = 0;
  159. textArea.style.border = 'none';
  160. textArea.style.outline = 'none';
  161. textArea.style.boxShadow = 'none';
  162. textArea.style.background = 'transparent';
  163. textArea.value = text;
  164. document.body.appendChild(textArea);
  165. textArea.select();
  166. try {
  167. var successful = document.execCommand('copy');
  168. var msg = successful ? 'successful' : 'unsuccessful';
  169. console.log('Copying text command was ' + msg);
  170. } catch (err) {
  171. console.log('Oops, unable to copy');
  172. }
  173. document.body.removeChild(textArea);
  174. }
  175.  
  176. })();