From 18c0db6c4fba86fb125f34901b7dc2f4d612a624 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lorenz=20H=C3=BCbschle-Schneider?= Date: Sun, 8 Feb 2015 11:16:55 +0100 Subject: [PATCH 1/4] Fix Asciinema plugin --- js/plugins.js | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/js/plugins.js b/js/plugins.js index fd9bf51..1d971cd 100644 --- a/js/plugins.js +++ b/js/plugins.js @@ -312,15 +312,21 @@ plugins.factory('userPlugins', function() { /* * Asciinema plugin */ - var asciinemaPlugin = new Plugin(function(message) { - - var regexp = /^https?:\/\/(www\.)?asciinema.org\/a\/(\d+)/; - var match = message.match(regexp); + var asciinemaPlugin = new Plugin(urlPlugin(function(url) { + var regexp = /^https?:\/\/(?:www\.)?asciinema.org\/a\/(\d+)/i; + var match = url.match(regexp); if (match) { - var id = match[3]; - return ""; + var id = match[1]; + return function() { + var element = this.getElement(); + var scriptElem = document.createElement('script'); + scriptElem.src = 'https://asciinema.org/a/' + id + '.js'; + scriptElem.id = 'asciicast-' + id; + scriptElem.async = true; + element.appendChild(scriptElem); + }; } - }); + })); asciinemaPlugin.name = "ascii cast"; var yrPlugin = new Plugin( From beaa3426dd929df1c6a08b9cce05dbf4def25b81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lorenz=20H=C3=BCbschle-Schneider?= Date: Sun, 8 Feb 2015 11:17:06 +0100 Subject: [PATCH 2/4] Rewrite vine plugin with urlPlugin --- js/plugins.js | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/js/plugins.js b/js/plugins.js index 1d971cd..0b3211e 100644 --- a/js/plugins.js +++ b/js/plugins.js @@ -400,22 +400,17 @@ plugins.factory('userPlugins', function() { /* * Vine plugin */ - var vinePlugin = new Plugin(function(message) { - - var regexp = /https?:\/\/(www\.)?vine.co\/v\/([a-zA-Z0-9]+)(\/.*)?/g; - var content = []; - var match; - - // Iterate over all matches - while ((match = regexp.exec(message)) !== null) { - var id = match[2]; - var embedurl = "https://vine.co/v/" + id + "/embed/simple?audio=1"; - content.push(''); - } - - return content; - }); - vinePlugin.name = "Vine"; + var vinePlugin = new Plugin( + urlPlugin(function (url) { + var regexp = /^https?:\/\/(www\.)?vine.co\/v\/([a-zA-Z0-9]+)(\/.*)?/i, + match = url.match(regexp); + if (match) { + var id = match[2], embedurl = "https://vine.co/v/" + id + "/embed/simple?audio=1"; + return ''; + } + }) + ); + vinePlugin.name = 'Vine'; return { plugins: [youtubePlugin, dailymotionPlugin, allocinePlugin, imagePlugin, spotifyPlugin, cloudmusicPlugin, googlemapPlugin, asciinemaPlugin, yrPlugin, gistPlugin, tweetPlugin, vinePlugin] From 4b65847b468badc4d2e43edcd930ffbd0993359c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lorenz=20H=C3=BCbschle-Schneider?= Date: Sun, 8 Feb 2015 11:23:32 +0100 Subject: [PATCH 3/4] Rewrite youtube plugin with urlPlugin Remove URL without protocol from tests --- js/plugins.js | 19 ++++++------------- test/unit/plugins.js | 1 - 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/js/plugins.js b/js/plugins.js index 0b3211e..5249b63 100644 --- a/js/plugins.js +++ b/js/plugins.js @@ -189,23 +189,16 @@ plugins.factory('userPlugins', function() { * * See: https://developers.google.com/youtube/player_parameters */ - var youtubePlugin = new Plugin(function(message) { + var youtubePlugin = new Plugin(urlPlugin(function(url) { + var regex = /(?:youtube.com|youtu.be)\/(?:v\/|embed\/|watch(?:\?v=|\/))?([a-zA-Z0-9-]+)/i, + match = url.match(regex); - var regExp = /(?:https?:\/\/)?(?:www\.)?(?:youtube.com|youtu.be)\/(?:v\/|embed\/|watch(?:\?v=|\/))?([a-zA-Z0-9-]+)/gm; - var match = regExp.exec(message); - var content = []; - - // iterate over all matches - while (match !== null){ + if (match){ var token = match[1]; var embedurl = "https://www.youtube.com/embed/" + token + "?html5=1&iv_load_policy=3&modestbranding=1&rel=0&showinfo=0"; - content.push(''); - // next match - match = regExp.exec(message); + return ''; } - - return content; - }); + })); youtubePlugin.name = 'YouTube video'; /* diff --git a/test/unit/plugins.js b/test/unit/plugins.js index af3229c..537f86a 100644 --- a/test/unit/plugins.js +++ b/test/unit/plugins.js @@ -47,7 +47,6 @@ describe('filter', function() { 'https://youtu.be/J6vIS8jb6Fs', 'http://www.youtube.com/embed/dQw4w9WgXcQ', 'https://www.youtube.com/embed/dQw4w9WgXcQ', - 'youtu.be/dQw4w9WgXcQ' ], 'YouTube video', plugins); From c57911db6477cb24c55a5599eb61f3056e0759e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lorenz=20H=C3=BCbschle-Schneider?= Date: Sun, 8 Feb 2015 11:08:12 +0100 Subject: [PATCH 4/4] Restructure plugin constructors to make them more convenient --- js/plugins.js | 307 ++++++++++++++++++++++++-------------------------- 1 file changed, 145 insertions(+), 162 deletions(-) diff --git a/js/plugins.js b/js/plugins.js index 5249b63..296fa0e 100644 --- a/js/plugins.js +++ b/js/plugins.js @@ -10,15 +10,42 @@ var plugins = angular.module('plugins', []); /* * Definition of a user provided plugin with sensible default values * - * User plugins are created by providing a contentForMessage function - * that parses a string and return any additional content. + * User plugins are created by providing a name and a contentForMessage + * function that parses a string and returns any additional content. */ -var Plugin = function(contentForMessage) { - +var Plugin = function(name, contentForMessage) { return { contentForMessage: contentForMessage, exclusive: false, - name: "additional content" + name: name + }; +}; + + +// Regular expression that detects URLs for UrlPlugin +var urlRegexp = /(?:ftp|https?):\/\/\S*[^\s.;,(){}<>]/g; +/* + * Definition of a user provided plugin that consumes URLs + * + * URL plugins are created by providing a name and a function that + * that parses a URL and returns any additional content. + */ +var UrlPlugin = function(name, urlCallback) { + return { + contentForMessage: function(message) { + var urls = message.match(urlRegexp); + var content = []; + + for (var i = 0; urls && i < urls.length; i++) { + var result = urlCallback(urls[i]); + if (result) { + content.push(result); + } + } + return content; + }, + exclusive: false, + name: name }; }; @@ -32,8 +59,6 @@ var Plugin = function(contentForMessage) { */ plugins.service('plugins', ['userPlugins', '$sce', function(userPlugins, $sce) { - var nsfwRegexp = new RegExp('nsfw', 'i'); - /* * Defines the plugin manager object */ @@ -52,6 +77,8 @@ plugins.service('plugins', ['userPlugins', '$sce', function(userPlugins, $sce) { } }; + var nsfwRegexp = new RegExp('nsfw', 'i'); + /* * Iterates through all the registered plugins * and run their contentForMessage function. @@ -146,23 +173,6 @@ plugins.factory('userPlugins', function() { document.body.appendChild(script); }; - var urlRegexp = new RegExp(/(?:ftp|https?):\/\/\S*[^\s.;,(){}<>]/g); - - var urlPlugin = function(callback) { - return function(message) { - var urls = message.match(urlRegexp); - var content = []; - - for (var i = 0; urls && i < urls.length; i++) { - var result = callback(urls[i]); - if (result) { - content.push(result); - } - } - return content; - }; - }; - /* * Spotify Embedded Player * @@ -170,7 +180,7 @@ plugins.factory('userPlugins', function() { * */ - var spotifyPlugin = new Plugin(function(message) { + var spotifyPlugin = new Plugin('Spotify track', function(message) { var content = []; var addMatch = function(match) { for (var i = 0; match && i < match.length; i++) { @@ -182,32 +192,29 @@ plugins.factory('userPlugins', function() { addMatch(message.match(/open.spotify.com\/track\/([a-zA-Z-0-9]{22})/g)); return content; }); - spotifyPlugin.name = 'Spotify track'; /* * YouTube Embedded Player * * See: https://developers.google.com/youtube/player_parameters */ - var youtubePlugin = new Plugin(urlPlugin(function(url) { + var youtubePlugin = new UrlPlugin('YouTube video', function(url) { var regex = /(?:youtube.com|youtu.be)\/(?:v\/|embed\/|watch(?:\?v=|\/))?([a-zA-Z0-9-]+)/i, match = url.match(regex); if (match){ - var token = match[1]; - var embedurl = "https://www.youtube.com/embed/" + token + "?html5=1&iv_load_policy=3&modestbranding=1&rel=0&showinfo=0"; + var token = match[1], + embedurl = "https://www.youtube.com/embed/" + token + "?html5=1&iv_load_policy=3&modestbranding=1&rel=0&showinfo=0"; return ''; } - })); - youtubePlugin.name = 'YouTube video'; + }); /* * Dailymotion Embedded Player * * See: http://www.dailymotion.com/doc/api/player.html */ - var dailymotionPlugin = new Plugin(function(message) { - + var dailymotionPlugin = new Plugin('Dailymotion video', function(message) { var rPath = /dailymotion.com\/.*video\/([^_?# ]+)/; var rAnchor = /dailymotion.com\/.*#video=([^_& ]+)/; var rShorten = /dai.ly\/([^_?# ]+)/; @@ -221,13 +228,11 @@ plugins.factory('userPlugins', function() { return null; }); - dailymotionPlugin.name = 'Dailymotion video'; /* * AlloCine Embedded Player */ - var allocinePlugin = new Plugin(function(message) { - + var allocinePlugin = new Plugin('AlloCine video', function(message) { var rVideokast = /allocine.fr\/videokast\/video-(\d+)/; var rCmedia = /allocine.fr\/.*cmedia=(\d+)/; @@ -240,74 +245,65 @@ plugins.factory('userPlugins', function() { return null; }); - allocinePlugin.name = 'AlloCine video'; /* * Image Preview */ - var imagePlugin = new Plugin( - urlPlugin(function(url) { - var embed = false; - // Check the get parameters as well, they might contain an image to load - var segments = url.split(/[?&]/).forEach(function(param) { - if (param.match(/\.(png|gif|jpg|jpeg)(:(small|medium|large))?$/i)) { - embed = true; - } - }); - if (embed) { - /* A fukung.net URL may end by an image extension but is not a direct link. */ - if (url.indexOf("^https?://fukung.net/v/") != -1) { - url = url.replace(/.*\//, "http://media.fukung.net/imgs/"); - } else if (url.match(/^http:\/\/(i\.)?imgur\.com\//i)) { - // remove protocol specification to load over https if used by g-b - url = url.replace(/http:/, ""); - } else if (url.match(/^https:\/\/www\.dropbox\.com\/s\/[a-z0-9]+\/[^?]+$/i)) { - // Dropbox requires a get parameter, dl=1 - url = url + "?dl=1"; - } - - return ''; + var imagePlugin = new UrlPlugin('image', function(url) { + var embed = false; + // Check the get parameters as well, they might contain an image to load + var segments = url.split(/[?&]/).forEach(function(param) { + if (param.match(/\.(png|gif|jpg|jpeg)(:(small|medium|large))?$/i)) { + embed = true; } - }) - ); - imagePlugin.name = 'image'; + }); + if (embed) { + /* A fukung.net URL may end by an image extension but is not a direct link. */ + if (url.indexOf("^https?://fukung.net/v/") != -1) { + url = url.replace(/.*\//, "http://media.fukung.net/imgs/"); + } else if (url.match(/^http:\/\/(i\.)?imgur\.com\//i)) { + // remove protocol specification to load over https if used by g-b + url = url.replace(/http:/, ""); + } else if (url.match(/^https:\/\/www\.dropbox\.com\/s\/[a-z0-9]+\/[^?]+$/i)) { + // Dropbox requires a get parameter, dl=1 + // TODO strip an existing dl=0 parameter + url = url + "?dl=1"; + } + + return ''; + } + }); /* * Cloud Music Embedded Players */ - var cloudmusicPlugin = new Plugin( - urlPlugin(function(url) { - /* SoundCloud http://help.soundcloud.com/customer/portal/articles/247785-what-widgets-can-i-use-from-soundcloud- */ - if (url.match(/^https?:\/\/soundcloud.com\//)) { - return ''; - } + var cloudmusicPlugin = new UrlPlugin('cloud music', function(url) { + /* SoundCloud http://help.soundcloud.com/customer/portal/articles/247785-what-widgets-can-i-use-from-soundcloud- */ + if (url.match(/^https?:\/\/soundcloud.com\//)) { + return ''; + } - /* MixCloud */ - if (url.match(/^https?:\/\/([a-z]+\.)?mixcloud.com\//)) { - return ''; - } - }) - ); - cloudmusicPlugin.name = 'cloud music'; + /* MixCloud */ + if (url.match(/^https?:\/\/([a-z]+\.)?mixcloud.com\//)) { + return ''; + } + }); /* * Google Maps */ - var googlemapPlugin = new Plugin( - urlPlugin(function(url) { - if (url.match(/^https?:\/\/maps\.google\./i) || url.match(/^https?:\/\/(?:[\w]+\.)?google\.[\w]+\/maps/i)) { - return ''; - } - }) - ); - googlemapPlugin.name = 'Google Map'; + var googlemapPlugin = new UrlPlugin('Google Map', function(url) { + if (url.match(/^https?:\/\/maps\.google\./i) || url.match(/^https?:\/\/(?:[\w]+\.)?google\.[\w]+\/maps/i)) { + return ''; + } + }); /* * Asciinema plugin */ - var asciinemaPlugin = new Plugin(urlPlugin(function(url) { - var regexp = /^https?:\/\/(?:www\.)?asciinema.org\/a\/(\d+)/i; - var match = url.match(regexp); + var asciinemaPlugin = new UrlPlugin('ascii cast', function(url) { + var regexp = /^https?:\/\/(?:www\.)?asciinema.org\/a\/(\d+)/i, + match = url.match(regexp); if (match) { var id = match[1]; return function() { @@ -319,91 +315,78 @@ plugins.factory('userPlugins', function() { element.appendChild(scriptElem); }; } - })); - asciinemaPlugin.name = "ascii cast"; + }); - var yrPlugin = new Plugin( - urlPlugin(function(url) { - var regexp = /^https?:\/\/(?:www\.)?yr\.no\/(place|stad|sted|sadji|paikka)\/(([^\s.;,(){}<>\/]+\/){3,})/; - var match = url.match(regexp); - if (match) { - var language = match[1]; - var location = match[2]; - var city = match[match.length - 1].slice(0, -1); - url = "http://www.yr.no/" + language + "/" + location + "avansert_meteogram.png"; - return "Meteogram for " + city + ""; - } - }) - ); - yrPlugin.name = "meteogram"; + var yrPlugin = new UrlPlugin('meteogram', function(url) { + var regexp = /^https?:\/\/(?:www\.)?yr\.no\/(place|stad|sted|sadji|paikka)\/(([^\s.;,(){}<>\/]+\/){3,})/; + var match = url.match(regexp); + if (match) { + var language = match[1]; + var location = match[2]; + var city = match[match.length - 1].slice(0, -1); + url = "http://www.yr.no/" + language + "/" + location + "avansert_meteogram.png"; + return "Meteogram for " + city + ""; + } + }); // Embed GitHub gists - var gistPlugin = new Plugin( - urlPlugin(function(url) { - var regexp = /^https:\/\/gist\.github.com\/[^.?]+/i; - var match = url.match(regexp); - if (match) { - // get the URL from the match to trim away pseudo file endings and request parameters - url = match[0] + '.json'; - // load gist asynchronously -- return a function here - return function() { - var element = this.getElement(); - jsonp(url, function(data) { - // Add the gist stylesheet only once - if (document.querySelectorAll('link[rel=stylesheet][href="' + data.stylesheet + '"]').length < 1) { - var stylesheet = ''; - document.getElementsByTagName('head')[0].innerHTML += stylesheet; - } - element.innerHTML = '
' + data.div + '
'; - }); - }; - } - }) - ); - gistPlugin.name = 'Gist'; + var gistPlugin = new UrlPlugin('Gist', function(url) { + var regexp = /^https:\/\/gist\.github.com\/[^.?]+/i; + var match = url.match(regexp); + if (match) { + // get the URL from the match to trim away pseudo file endings and request parameters + url = match[0] + '.json'; + // load gist asynchronously -- return a function here + return function() { + var element = this.getElement(); + jsonp(url, function(data) { + // Add the gist stylesheet only once + if (document.querySelectorAll('link[rel=stylesheet][href="' + data.stylesheet + '"]').length < 1) { + var stylesheet = ''; + document.getElementsByTagName('head')[0].innerHTML += stylesheet; + } + element.innerHTML = '
' + data.div + '
'; + }); + }; + } + }); - var tweetPlugin = new Plugin( - urlPlugin(function(url) { - var regexp = /^https?:\/\/twitter\.com\/(?:#!\/)?(\w+)\/status(?:es)?\/(\d+)/i; - var match = url.match(regexp); - if (match) { - url = 'https://api.twitter.com/1/statuses/oembed.json?id=' + match[2]; - return function() { - var element = this.getElement(); - jsonp(url, function(data) { - // sepearate the HTML into content and script tag - var scriptIndex = data.html.indexOf("'; - } - }) - ); - vinePlugin.name = 'Vine'; + var vinePlugin = new UrlPlugin('Vine', function (url) { + var regexp = /^https?:\/\/(www\.)?vine.co\/v\/([a-zA-Z0-9]+)(\/.*)?/i, + match = url.match(regexp); + if (match) { + var id = match[2], embedurl = "https://vine.co/v/" + id + "/embed/simple?audio=1"; + return ''; + } + }); return { plugins: [youtubePlugin, dailymotionPlugin, allocinePlugin, imagePlugin, spotifyPlugin, cloudmusicPlugin, googlemapPlugin, asciinemaPlugin, yrPlugin, gistPlugin, tweetPlugin, vinePlugin]