Embed Me!

An userscript to embed video, images from links.

当前为 2015-06-18 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Embed Me!
  3. // @author eight <eight04@gmail.com>
  4. // @homepage https://github.com/eight04/Embed-Me
  5. // @supportURL https://github.com/eight04/Embed-Me/issues
  6. // @compatible firefox
  7. // @compatible chrome
  8. // @compatible opera
  9. // @version 0.2.0
  10. // @namespace eight04.blogspot.com
  11. // @description An userscript to embed video, images from links.
  12. // @include http*
  13. // @require https://greasyfork.org/scripts/7212-gm-config-eight-s-version/code/GM_config%20(eight's%20version).js?version=57385
  14. // @grant GM_addStyle
  15. // @grant GM_registerMenuCommand
  16. // @grant GM_getValue
  17. // @grant GM_setValue
  18. // @grant GM_xmlhttpRequest
  19. // @license MIT
  20. // ==/UserScript==
  21.  
  22. var embedMe = function(){
  23.  
  24. "use strict";
  25.  
  26. if (location.hash.indexOf("embed-me") >= 0) {
  27. return null;
  28. }
  29.  
  30. var globalMods = [],
  31. index = {},
  32. config, re;
  33.  
  34. GM_config.init("Embed Me!", {
  35. simple: {
  36. label: "Ignore complex anchor",
  37. type: "checkbox",
  38. default: true
  39. },
  40. excludes: {
  41. label: "Excludes these urls (regexp per line)",
  42. type: "textarea",
  43. default: ""
  44. }
  45. });
  46.  
  47. function loadConfig() {
  48. config = GM_config.get();
  49. var exclude = config.excludes.trim();
  50. re = {
  51. excludeUrl: exclude && new RegExp(exclude.split(/\s*\n\s*/).join("|"), "i")
  52. };
  53. }
  54.  
  55. function addModule(modProvider) {
  56. var mod = modProvider();
  57.  
  58. if (mod.global) {
  59. globalMods.push(mod);
  60. } else {
  61. var i;
  62. for (i = 0; i < mod.domains.length; i++) {
  63. index[mod.domains[i]] = mod;
  64. }
  65. }
  66. }
  67.  
  68. function validParent(node) {
  69. var cache = node;
  70. while (node != document.documentElement) {
  71. if (node.INVALID || node.className.indexOf("embed-me") >= 0) {
  72. cache.INVALID = true;
  73. return false;
  74. }
  75. if (!node.parentNode) {
  76. return false;
  77. }
  78. if (node.VALID) {
  79. break;
  80. }
  81. node = node.parentNode;
  82. }
  83. cache.VALID = true;
  84. return true;
  85. }
  86.  
  87. function valid(node) {
  88. if (!validParent(node)) {
  89. return false;
  90. }
  91. if (node.nodeName != "A" || !node.href) {
  92. return false;
  93. }
  94. if (config.simple && (node.childNodes.length != 1 || node.childNodes[0].nodeType != 3)) {
  95. return false;
  96. }
  97. if (re.excludeUrl && re.excludeUrl.test(node.href)) {
  98. return false;
  99. }
  100. return true;
  101. }
  102.  
  103. function getPatterns(mod) {
  104. if (!mod.getPatterns) {
  105. return [];
  106. }
  107. if (!mod.patterns) {
  108. mod.patterns = mod.getPatterns();
  109. }
  110. return mod.patterns;
  111. }
  112.  
  113. function getEmbedFunction(mod) {
  114. if (!mod.embedFunction) {
  115. mod.embedFunction = mod.getEmbedFunction();
  116. }
  117. return mod.embedFunction;
  118. }
  119.  
  120. function callEmbedFunc(node, params, func) {
  121. var replace = function (newNode) {
  122. if (!node.parentNode) {
  123. // The node was detached from DOM tree
  124. return;
  125. }
  126. newNode.classList.add("embed-me");
  127. node.parentNode.replaceChild(newNode, node);
  128. };
  129. params.push(node.href, node.textContent, node, replace);
  130. var result = func.apply(null, params);
  131. if (result) {
  132. replace(result);
  133. }
  134. }
  135.  
  136. function embed(node) {
  137. if (!valid(node)) {
  138. return;
  139. }
  140. // Never process same element twice
  141. node.INVALID = true;
  142.  
  143. var mods = [], mod, patterns, match, i, j;
  144.  
  145. if (node.hostname in index) {
  146. mods.push(index[node.hostname]);
  147. }
  148.  
  149. mods = mods.concat(globalMods);
  150.  
  151. for (j = 0; j < mods.length; j++) {
  152. mod = mods[j];
  153. patterns = getPatterns(mod);
  154.  
  155. for (i = 0; i < patterns.length; i++) {
  156. if ((match = patterns[i].exec(node.href))) {
  157. callEmbedFunc(node, Array.prototype.slice.call(match, 1), getEmbedFunction(mod));
  158. return;
  159. }
  160. }
  161. }
  162. }
  163.  
  164. function observeDocument(callback) {
  165.  
  166. setTimeout(callback, 0, document.body);
  167.  
  168. new MutationObserver(function(mutations){
  169. var i;
  170. for (i = 0; i < mutations.length; i++) {
  171. if (!mutations[i].addedNodes.length) {
  172. continue;
  173. }
  174. callback(mutations[i].target);
  175. }
  176. }).observe(document.body, {
  177. childList: true,
  178. subtree: true
  179. });
  180. }
  181.  
  182. function init() {
  183. observeDocument(function(node){
  184. var links = node.querySelectorAll("a[href]"), i;
  185. for (i = 0; i < links.length; i++) {
  186. embed(links[i]);
  187. }
  188. });
  189. }
  190.  
  191. loadConfig();
  192.  
  193. GM_registerMenuCommand("Embed Me! - Configure", GM_config.open);
  194. GM_config.onclose = loadConfig;
  195.  
  196. init();
  197.  
  198. return {
  199. addModule: addModule
  200. };
  201. }();
  202.  
  203.  
  204. embedMe.addModule(function(){
  205. "use strict";
  206. return {
  207. name: "Gfycat",
  208. domains: ["gfycat.com"],
  209. getPatterns: function() {
  210. return [
  211. /gfycat\.com\/([A-Z]\w*)$/i
  212. ];
  213. },
  214. getEmbedFunction: function() {
  215. return function(name, url, text, node, replace) {
  216. GM_xmlhttpRequest({
  217. method: "GET",
  218. url: "//gfycat.com/cajax/get/" + name,
  219. onload: function(response) {
  220. var res = JSON.parse(response.responseText);
  221. if (res.error) {
  222. return;
  223. }
  224. var video = document.createElement("video");
  225. video.autoplay = true;
  226. video.loop = true;
  227. video.src = res.gfyItem.mp4Url;
  228. video.title = text;
  229. replace(video);
  230. }
  231. });
  232. };
  233. }
  234. };
  235. });
  236.  
  237. embedMe.addModule(function(){
  238. "use strict";
  239.  
  240. return {
  241. name: "Image",
  242. global: true,
  243. getPatterns: function() {
  244. return [
  245. /^[^?#]+\.(?:jpg|png|gif|jpeg)(?:$|[?#])/i
  246. ];
  247. },
  248. getEmbedFunction: function() {
  249. GM_addStyle(".embed-me-image { max-width: 90%; }");
  250. return function(url, text, node) {
  251. var image = new Image;
  252. image.title = text;
  253. image.className = "embed-me-image";
  254. image.src = url;
  255. node = node.cloneNode(false);
  256. node.appendChild(image);
  257. return node;
  258. };
  259. }
  260. };
  261. });
  262.  
  263. embedMe.addModule(function(){
  264. "use strict";
  265. return {
  266. name: "Imgur gifv",
  267. domains: ["i.imgur.com", "imgur.com"],
  268. getPatterns: function() {
  269. return [
  270. /imgur\.com\/(\w+)(\.gifv|$)/i
  271. ];
  272. },
  273. getEmbedFunction: function() {
  274. GM_addStyle('.imgur-embed-iframe-pub { box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.10); border: 1px solid #ddd; border-radius: 2px; margin: 10px 0; width: 540px; overflow: hidden; }');
  275.  
  276. window.addEventListener("message", function(e){
  277. if (e.origin.indexOf("imgur.com") < 0) {
  278. return;
  279. }
  280.  
  281. var data = JSON.parse(e.data),
  282. id = data.href.match(/imgur\.com\/(\w+)\//)[1],
  283. css = '.imgur-embed-iframe-pub-' + id + '-' + data.context + '-540 { height: ' + data.height + 'px!important; width: 540px!important; }';
  284.  
  285. GM_addStyle(css);
  286. });
  287.  
  288. return function(id) {
  289. var iframe = document.createElement("iframe");
  290. iframe.className = "imgur-embed-iframe-pub imgur-embed-iframe-pub-" + id + "-true-540";
  291. iframe.scrolling = "no";
  292. iframe.src = "//imgur.com/" + id + "/embed?w=540&ref=" + location.href + "#embed-me";
  293. return iframe;
  294. };
  295. }
  296. };
  297. });
  298.  
  299. embedMe.addModule(function(){
  300. "use strict";
  301. return {
  302. name: "SoundCloud",
  303. domains: ["soundcloud.com"],
  304. getPatterns: function() {
  305. return [
  306. /soundcloud\.com\/[\w-]+\/[\w-]+(?:\?|$)/i
  307. ];
  308. },
  309. getEmbedFunction: function(){
  310. return function(url, text, node, replace) {
  311. GM_xmlhttpRequest({
  312. method: "GET",
  313. url: "//soundcloud.com/oembed?format=json&url=" + url,
  314. onload: function(response) {
  315. if (!response.responseText) {
  316. return;
  317. }
  318. var html = JSON.parse(response.responseText).html;
  319. var container = document.createElement("div");
  320. container.innerHTML = html;
  321. replace(container);
  322. }
  323. });
  324. };
  325. }
  326. };
  327. });
  328.  
  329. embedMe.addModule(function(){
  330. "use strict";
  331. return {
  332. name: "Twitch",
  333. domains: ["www.twitch.tv"],
  334. getPatterns: function() {
  335. return [
  336. /twitch\.tv\/(\w+)\/v\/(\d+)/i
  337. ];
  338. },
  339. getEmbedFunction: function() {
  340. return function (user, id) {
  341. var container = document.createElement("div");
  342. container.innerHTML = '<object bgcolor="#000000" data="http://www.twitch.tv/swflibs/TwitchPlayer.swf" height="378" id="clip_embed_player_flash" type="application/x-shockwave-flash" width="620"><param name="movie" value="http://www.twitch.tv/swflibs/TwitchPlayer.swf" /><param name="allowScriptAccess" value="always" /><param name="allowNetworking" value="all" /><param name="allowFullScreen" value="true" /><param name="flashvars" value="channel=' + user + '&amp;auto_play=false&amp;autoplay=false&amp;autostart=false&amp;start_volume=25&amp;videoId=v' + id + '" /></object><br /><a href="http://www.twitch.tv/' + user + '" style="padding:2px 0px 4px; display:block; width: 320px; font-weight:normal; font-size:10px; text-decoration:underline;">Watch live video from ' + user + ' on Twitch</a>';
  343. return container;
  344. };
  345. }
  346. };
  347. });
  348.  
  349. embedMe.addModule(function(){
  350. "use strict";
  351. return {
  352. name: "Video",
  353. global: true,
  354. getPatterns: function() {
  355. return [
  356. /^[^?#]+\.(?:mp4|webm|ogv|mov)(?:$|[?#])/i
  357. ];
  358. },
  359. getEmbedFunction: function() {
  360. return function (url, text) {
  361. var video = document.createElement("video");
  362. video.controls = true;
  363. video.title = text;
  364. video.src = url;
  365. return video;
  366. };
  367. }
  368. };
  369. });
  370.  
  371. embedMe.addModule(function(){
  372. "use strict";
  373.  
  374. return {
  375. name: "Youtube",
  376. domains: [
  377. "www.youtube.com",
  378. "youtu.be"
  379. ],
  380. getPatterns: function() {
  381. return [
  382. /youtube\.com\/watch\?v=([^&]+)/i,
  383. /youtu\.be\/([^?]+)/i
  384. ];
  385. },
  386. getEmbedFunction: function() {
  387. return function(id, url, text, node, replace) {
  388. GM_xmlhttpRequest({
  389. method: "GET",
  390. url: "//www.youtube.com/oembed?format=json&url=" + url,
  391. onload: function(response) {
  392. var html = JSON.parse(response.responseText).html,
  393. container = document.createElement("div");
  394.  
  395. container.innerHTML = html;
  396. replace(container);
  397. }
  398. });
  399. };
  400. }
  401. };
  402. });